阅读 973

Android 开发:apk文件的安装6.0,7.0,8.0踩坑笔记

最近在做 APP 的升级功能,需要在 apk 文件下载下来之后安装,我本以为是个很简单的功能,直接调用系统的接口就完事了,没想到还是有不少坑的。

Android 6.0 版本的安装

这个最常规,在6.0版本及之前版本都是通用的。

/**
 * 安装apk
 *
 * @param
 */
public static void install(Context mContext, File file) {
    Intent install = new Intent(Intent.ACTION_VIEW);
    install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    install.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
    mContext.startActivity(install);
}
复制代码

Android 7.0 版本的安装

上面的代码在 Android 7.0 及以上版本安装 apk 则会遇到 android.os.FileUriExposedException 问题。官方给出的解释是:

对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode API 政策禁止在您的应用外部公开 file:// URI。如果一项包含文件 URI 的 intent 离开您的应用,则应用出现故障,并出现 FileUriExposedException 异常。

解决方案如下:

1.在 AndroidManifest.xml 中添加如下代码:

<provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="你的包名.fileProvider"
            android:grantUriPermissions="true"
            android:exported="false">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
复制代码

注意

authorities:你app的包名.fileProvider
grantUriPermissions:必须是true,表示授予 URI 临时访问权限
exported:必须是false
resource:中的@xml/file_paths是我们接下来要添加的文件
复制代码

还有额外的坑☠️

我在使用过程中添加以上 provider 代码之后,遇到了 manifest 合并冲突问题:

Error:Execution failed for task ':app:processDebugManifest'.
> Manifest merger failed with multiple errors, see logs
复制代码

解决方案:由于多个lib库中定义类相同所以冲突,集成FileProvider重写个类即可。没有遇到此问题则可以略过。代码如下:

public class InstallApkProvider extends FileProvider {
}
复制代码

所以我在 Manifest 中添加的代码如下:

<provider
    android:name=".InstallApkProvider"
    android:authorities="我的包名.fileProvider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>
复制代码

2.在res/xml下新建file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <root-path name="root" path="" />
        <files-path name="files" path="" />
        <cache-path name="cache" path="" />
        <external-path name="external" path="" />
        <external-files-path name="external_file_path" path="" />
        <external-cache-path name="external_cache_path" path="" />
    </paths>
</resources>
复制代码

注意

path:需要临时授权访问的路径
name:就是你给这个访问路径起个名字
复制代码

3.Android 7.0 的构建 Uri 在6.0时代,直接使用 Uri.fromFile(apkFile)构建出一个Uri,现在我们使用

FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", apkFile);
复制代码

BuildConfig.APPLICATION_ID 直接是应用的包名。

到这里,我们就可以愉快的在7.0上安装 apk 了。代码如下:

    /**
     * 安装apk
     *
     * @param
     */
    public static void install(Context mContext, File file) {
        Intent install = new Intent(Intent.ACTION_VIEW);
        install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            Uri contentUri = FileProvider.getUriForFile(mContext, "com.skycar.vehicleassistor.fileProvider", file);

            install.setDataAndType(contentUri, "application/vnd.android.package-archive");
        } else {
            install.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
        }
        mContext.startActivity(install);
    }
复制代码

Android 8.0 版本的安装

如果你的系统版本是 8.0+,那你需要多加一个权限,否则还是不行滴!🤣

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
复制代码

总结

就这样,我们完全搞定了 apk 文件在不同 Android 版本系统上的安装。撒花🎉

关注下面的标签,发现更多相似文章
评论