以编程方式从 Android 5.0 - 6.0 中的应用程序内部存储启动 APK 安装 [英] Programatically launch an APK installation from app internal storage in Android 5.0 - 6.0

查看:19
本文介绍了以编程方式从 Android 5.0 - 6.0 中的应用程序内部存储启动 APK 安装的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 Android 7/api24 中以编程方式安装 apk回答了一个类似的问题,但我也想在 Android 5.0 - 6.0 中以编程方式从应用程序内部存储(在 SD 卡上)安装 APK.

Programmatically install an apk in Android 7 / api24 answers a similar question, but I want to programmatically install an APK from app internal storage (on an SD card) in Android 5.0 - 6.0 too.

如果不是来自应用程序内部存储,我可以 startActivity() 使用其操作为 ACTION_VIEW 并且其数据 URI 具有 文件的 Intent:// 方案(使用 Uri.fromFile()).但我已经尝试过了,它不适用于其他应用无法读取的应用内部存储.我基于这样的日志消息

If it weren't from app internal storage, I could startActivity() with an intent whose action is ACTION_VIEW and whose data URI has a file:// scheme (using Uri.fromFile()). But I've tried that and it won't work with app internal storage, which isn't readable by other apps. I'm basing this on log messages like

W/asset: Asset path /storage/C5DF-1113/Android/data/com.example/files/foo.apk is neither a directory nor file (type=1).
W/InstallFlowAnalytics: Failed to hash APK contents
  java.io.FileNotFoundException: /storage/C5DF-1113/Android/data/com.example/files/foo.apk: open failed: ENOENT (No such file or directory)

我尝试使用 content:// 方案对 ContentProvider 进行子类化.但显然(并参见 CommonsWare 的答案在这里),软件包安装程序不支持 content:// 方案直到 Android 7.0.我在 Android 6.0 中收到如下日志消息:

I've tried subclassing ContentProvider, using a content:// scheme. But apparently (and see CommonsWare's answer here), the package installer doesn't support the content:// scheme until Android 7.0. I get log messages like the following in Android 6.0:

I/ActivityManager: START u0 {act=android.intent.action.VIEW dat=content://com.example.provider/internal/foo.apk typ=application/vnd.android.package-archive flg=0x10000001} from uid 10159 on display 0
E/Updates: installNewApk
     android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=content://com.example.provider/internal/foo.apk typ=application/vnd.android.package-archive flg=0x10000001 }
        at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1805)

与包安装程序匹配的意图和不匹配的意图之间唯一明显的区别是数据 URI 从 file://... 更改为 content://....

where the only apparent difference between an intent that matches the package installer and an intent that doesn't, is the data URI changing from file://... to content://....

其他地方有关于静默安装应用程序的答案,这不是我的项目的要求.也有一些解决方案使用反射来获取未记录的功能,或者需要设备 root,但这些技术对于这个项目来说太脆弱或对用户不友好.

There are answers elsewhere on installing an app silently, which is not a requirement for my project. There are also solutions that use reflection to get at undocumented features, or that require the device to be rooted, but those techniques are too brittle or user-unfriendly for this project.

还可以选择在启动安装程序之前将 APK 从应用内部存储复制到外部存储.我们可能不得不去那里,但首先将 APK 放在内部存储中的原因是出于隐私和安全的目的.(我意识到这远非万无一失,但总比没有好,这是我们的客户想要的.)

There is also the option to copy the APK from app internal storage to external storage before launching the installer. We might have to go there, but the reason the APK is in internal storage in the first place is for purposes of privacy and security. (I realize this is far from bulletproof, but it is better than nothing and it's what our clients want.)

我愿意基于目前的Android版本做一个conditional,但此时我仍然不知道在5.0-6.0中该做什么.

I'm willing to do a conditional based on the present version of Android, but at this point I still don't know what to do in 5.0 - 6.0.

感谢您的任何建议.

推荐答案

要从内部存储目录中获取 apk 文件的 Uri,您可以使用此方法对我有用:

to get the Uri for apk file from internal storage directory you can use this method it works me :

  public static Uri getApkUri(Activity activity, String path) {

    // Before N, a MODE_WORLD_READABLE file could be passed via the ACTION_INSTALL_PACKAGE
    // Intent. Since N, MODE_WORLD_READABLE files are forbidden, and a FileProvider is
    // recommended.
    boolean useFileProvider = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
    int fileMode = useFileProvider ? Context.MODE_PRIVATE : Context.MODE_WORLD_READABLE;

    String tempFilename = "tmp.apk";
    byte[] buffer = new byte[16384];

    try (InputStream is = new FileInputStream(path);
         FileOutputStream fout = activity.openFileOutput(tempFilename, fileMode)) {
        int n;
        while ((n = is.read(buffer)) >= 0) {
            fout.write(buffer, 0, n);
        }
    } catch (IOException e) {
        Logger.e(TAG, ":getApkUri + " + "Failed to write temporary APK file", e);
    }

    if (useFileProvider) {
        File toInstall = new File(activity.getFilesDir(), tempFilename);
        return FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID, toInstall);
    } else {
        return Uri.fromFile(activity.getFileStreamPath(tempFilename));
    }
}

这篇关于以编程方式从 Android 5.0 - 6.0 中的应用程序内部存储启动 APK 安装的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆