最近在做 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+,那你需要多加一个权限,否则还是不行滴!

相关文章
- Flutter日历2.0,支持月视图和周视图,可以支持自定义风格
- AndroidNetworkWatcher:简化网络监听业务代码
- 【源码解析】AsyncTask的用法与规则
- flutter - 登陆界面&表单校验
- Flutter实现Android、iOS跨平台经验总结
- 设计模式总结——《Android源码设计模式解析与实战》笔记
- Android源码剖析:基于 Handler、Looper 实现拦截全局崩溃、监控ANR等
- Flutter | 性能优化——如何避免应用 jank
上一篇: Activity启动模式一