封面圖還是要有的
之前使用Intent
安裝APK文件,使用下面的代碼可以直接調起安裝界面。
public void installAPK(Context context, File apkFile){
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.fromFile(apkFile),"application/vnd.android.package-archive");
context.startActivity(intent);
}
最近把targetSdkVersion
升級到了26才發現app在Android 8.0的手機上無法正常安裝,一查才知道從Android N(也就是Android API 24)開始直接執行上面的代碼會報android.os.FileUriExposedException
異常,需要使用FileProvider
來處理對Content URI讀取的臨時授權問題。直接上解決方案
首先,需要在AndroidManifest.xml
文件中的<application>
標籤中定義FileProvider
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true"
android:authorities="com.mrtrying.installappdemo.fileprovider">
...
</provider>
...
</application>
注意:這裏
<provider>
標籤中的android:authorities
爲設置授權的URI,一般都是授權自己app包名下的fileprovider
,也就是應用包名.fileprovider
;而android:exported
屬性爲false
則表示該FileProvider
不是需要是公共的。另外,android:grantUriPermissions
屬性爲true
表示允許給文件臨時訪問權限。
然後,在res/xml
目錄下定義一個apk_path.xml
文件,內容如下:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="apk_path"
path="."/>
</paths>
<paths>
中必須有子元素,這裏使用<external-path>
標籤對應的是使用Environment.getExternalStorageDirectory()
路徑,name
屬性則是名稱,path
則是表示臨時授權的子目錄路徑(不是單個文件),使用.
表示所有路徑,這個可以根據需求添加。
將定義好的apk_path.xml
文件通過<meta-data>
添加到之前在AndroidManifest.xml
文件中定義的<provider>
中,將<meta-data>
標籤的android:resource
屬性設置爲@xml/apk_path
<provider
android:name="android.support.v4.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true"
android:authorities="com.mrtrying.installappdemo.fileprovider">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/apk_path"/>
</provider>
最後,只需要在代碼中使用FileProvider
來獲取URI就大功告成了
public void installAPK(Context context, File apkFile){
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
//第二個參數需要與<provider>標籤中的android:authorities屬性相同
uri = FileProvider.getUriForFile(this,"com.mrtrying.installappdemo.fileProvider",apkFile);
}else{
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
uri = Uri.fromFile(apkFile);
}
intent.setDataAndType(Uri.fromFile(apkFile),"application/vnd.android.package-archive");
context.startActivity(intent);
}