简体   繁体   English

从内部存储器下载并安装应用程序 (apk) - 无 SD 卡

[英]Download and install an application (apk) from internal memory - no SD card

Greetings and a happy new year to all my fellow programmers.向我所有的程序员伙伴们问好,新年快乐。

My code downloads an apk file from a remote server.我的代码从远程服务器下载了一个 apk 文件。 I need to initiate the installation procedure through code, without user having to explicitly install it.我需要通过代码启动安装过程,而无需用户显式安装。 The catch is that i cannot use an SD card download the apk file.问题是我不能使用 SD 卡下载 apk 文件。

I can navigate to the data/data/files folder and can see my file downloaded.我可以导航到 data/data/files 文件夹并可以看到我下载的文件。 The only problem is that i cannot get it installed.唯一的问题是我无法安装它。 This is what i get这就是我得到的

 '/data/data/org.obs.testinstall.main/files/app.apk': Permission denied 

I understand that Android does not give permission to access the data directory.我了解 Android 不授予访问数据目录的权限。 My question is how can i download and install an application(apk) without using a SD card.我的问题是如何在不使用 SD 卡的情况下下载和安装应用程序(apk)。 This application is not intended to be published in the market.此应用程序不打算在市场上发布。 I have tried using both the Internal Storage using我尝试使用内部存储

FileOutputStream fos = openFileOutput("app.apk", Context.MODE_PRIVATE);

and the cache directory和缓存目录

File file = getCacheDir();
File outputFile = new File(file, "app.apk");

Both give the same result .. "Permission denied"两者都给出相同的结果..“权限被拒绝”

When i change the code to incorporate an SD card the application works perfectly, but using an SD card is not an option.当我更改代码以合并 SD 卡时,应用程序可以完美运行,但不能选择使用 SD 卡。

Surely there must be a way to do this.肯定有办法做到这一点。 It is hard to believe that such a handicap exist in the Android O/S.很难相信 Android 操作系统中存在这样的障碍。

Has anybody done this?有人做过吗? Any workarounds?任何解决方法? Any pointers would be helpful.任何指针都会有所帮助。

It it caused by android application can not read from another application file if it is written using PRIVATE mode.如果使用PRIVATE模式写入,它是由android应用程序引起的,无法从另一个应用程序文件中读取。

You can do this:你可以这样做:

String fileName = "tmp.apk";
FileOutputStream fos = openFileOutput(fileName,
        MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE);

// write the .apk content here ... flush() and close()

// Now start the standard instalation window
File fileLocation = new File(context.getFilesDir(), fileName);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(fileLocation),
                       "application/vnd.android.package-archive");
context.startActivity(intent);

Be careful though, because that file is now world-visible, and can be seen by any application in the same device, if they know the file location.不过要小心,因为该文件现在是全世界可见的,并且可以被同一设备中的任何应用程序看到,如果他们知道文件位置的话。

No need to root.无需root。 You can just use linux command chmod to do it.你可以使用 linux 命令 chmod 来完成它。

public static String exec(String[] args) {
    String result = "";
    ProcessBuilder processBuilder = new ProcessBuilder(args);
    Process process = null;
    InputStream errIs = null;
    InputStream inIs = null;
    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int read = -1;
        process = processBuilder.start();
        errIs = process.getErrorStream();
        while ((read = errIs.read()) != -1) {
            baos.write(read);
        }
        baos.write('\n');
        inIs = process.getInputStream();
        while ((read = inIs.read()) != -1) {
            baos.write(read);
        }
        byte[] data = baos.toByteArray();
        result = new String(data);
    } catch (IOException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (errIs != null) {
                errIs.close();
            }
            if (inIs != null) {
                inIs.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (process != null) {
            process.destroy();
        }
    }
    return result;
}

in your program,it can be invoked like this:在您的程序中,可以像这样调用它:

    String[] args1 = { "chmod", "705", "/data/data/org.obs.testinstall.main/files/" };
    exec(args1);
    String[] args2 = { "chmod", "604", "/data/data/org.obs.testinstall.main/files/app.apk" };
    exec(args2);

Then you can install the app.apk as wished.然后您可以根据需要安装 app.apk。

Also you can use你也可以使用

downloadedFile.setReadable(true, false);

with

fileOutputStream = openFileOutput(fileName, Context.MODE_PRIVATE);

There are two setReadable method.有两个 setReadable 方法。 The first has one parameter and the second one has two parameters.第一个有一个参数,第二个有两个参数。

setReadable(boolean readable)
setReadable(boolean readable, boolean ownerOnly)

For me I deleted the apk file right after the startActivity , which is asynchronous.对我来说,我在startActivity之后删除了 apk 文件,这是异步的。

Too bad there is no better description of the parsing error (file not found, access denied, corrupted file in package,...)太糟糕了,没有更好的解析错误描述(文件未找到,访问被拒绝,包中的文件损坏,......)

when you send intent to install apk, you can use this function to change mode for apk directory.当您发送安装 apk 的意图时,您可以使用此功能更改 apk 目录的模式。

private static boolean changeMode(String filePath, String prefixPath) {
    if (TextUtils.isEmpty(prefixPath) || !filePath.startsWith(prefixPath)) {
        return true;
    }

    try {
        String[] args1 = { "chmod", "705", prefixPath};
        Runtime.getRuntime().exec(args1);
    } catch (IOException e) {
        e.printStackTrace();
        return false;
    }

    String subPath = filePath.split(prefixPath)[1];
    String[] subArr = subPath.split(File.separator);
    for (String path : subArr) {
        if (!TextUtils.isEmpty(path)) {
            prefixPath = prefixPath + File.separator + path;
            try {
                if (!prefixPath.endsWith(".apk")) {
                    String[] progArray1 = {"chmod", "705", prefixPath};
                    Runtime.getRuntime().exec(progArray1);
                } else {
                    String[] progArray2 = {"chmod", "604", prefixPath};
                    Runtime.getRuntime().exec(progArray2);
                }
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
    }
    return true;
}

And before you send intent, check chmod is it alreay success.在您发送意图之前,请检查 chmod 是否已经成功。

    boolean chmodRes = changeMode(filePath, context.getCacheDir().getAbsolutePath())
            && changeMode(filePath, context.getFilesDir().getAbsolutePath());
    if (!chmodRes) {
        return false;
    }

尝试生根您的设备,然后从设备运行程序,而不是使用模拟器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM