最近在作 APP 的升級功能,須要在 apk 文件下載下來以後安裝,我本覺得是個很簡單的功能,直接調用系統的接口就完事了,沒想到仍是有很多坑的。php
這個最常規,在6.0版本及以前版本都是通用的。java
/** * 安裝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 及以上版本安裝 apk 則會遇到 android.os.FileUriExposedException 問題。官方給出的解釋是:android
對於面向 Android 7.0 的應用,Android 框架執行的 StrictMode API 政策禁止在您的應用外部公開 file:// URI。若是一項包含文件 URI 的 intent 離開您的應用,則應用出現故障,並出現 FileUriExposedException 異常。bash
解決方案以下:app
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>
複製代碼
注意ide
authorities:你app的包名.fileProvider
grantUriPermissions:必須是true,表示授予 URI 臨時訪問權限
exported:必須是false
resource:中的@xml/file_paths是咱們接下來要添加的文件
複製代碼
我在使用過程當中添加以上 provider 代碼以後,遇到了 manifest 合併衝突問題:ui
Error:Execution failed for task ':app:processDebugManifest'.
> Manifest merger failed with multiple errors, see logs
複製代碼
解決方案:因爲多個lib庫中定義類相同因此衝突,集成FileProvider重寫個類便可。沒有遇到此問題則能夠略過。代碼以下:spa
public class InstallApkProvider extends FileProvider {
}
複製代碼
因此我在 Manifest 中添加的代碼以下:code
<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);
}
複製代碼
若是你的系統版本是 8.0+,那你須要多加一個權限,不然仍是不行滴!🤣
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
複製代碼
就這樣,咱們徹底搞定了 apk 文件在不一樣 Android 版本系統上的安裝。撒花🎉