FileUriExposedException 在給app做版本升級的時候,先從伺服器下載新版本的apk文件到sdcard路徑,然後調用安裝apk的代碼,一般寫法如下: 這樣的寫法在Android7.0版本之前是沒有任何問題,只要給一個apk文件路徑就能打開安裝。但是在Android7.0版本上會報 ...
FileUriExposedException
在給app做版本升級的時候,先從伺服器下載新版本的apk文件到sdcard路徑,然後調用安裝apk的代碼,一般寫法如下:
private void openAPK(String fileSavePath){
File file=new File(fileSavePath);
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri data = Uri.fromFile(file);
intent.setDataAndType(data, "application/vnd.android.package-archive");
startActivity(intent);
}
這樣的寫法在Android7.0版本之前是沒有任何問題,只要給一個apk文件路徑就能打開安裝。但是在Android7.0版本上會報錯:
android.os.FileUriExposedException:
file:///storage/emulated/0/Download/FileProvider.apk
exposed beyond app through Intent.getData()
從Android 7.0開始,一個應用提供自身文件給其它應用使用時,如果給出一個file://格式的URI的話,應用會拋出FileUriExposedException。這是由於谷歌認為目標app可能不具有文件許可權,會造成潛在的問題。所以讓這一行為快速失敗。
FileProvider方式解決
這是谷歌官方推薦的解決方案。即使用FileProvider來生成一個content://格式的URI。
1.在Manifest.xml中聲明一個provider。
<application ···>
···
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.ansen.fileprovider.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
android:name值是固定的,android:authorities隨便寫但是必須得保證唯一性,我這邊用的是包名+"fileprovider",android:grantUriPermission跟android:exported固定值。
裡面包含一個meta-data標簽,這個標簽的name屬性固定寫法,android:resource對應的是一個xml文件。我們在res文件夾下新建一個xml文件夾,在xml文件夾下新建file_paths.xml文件。內容如下:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="name" path="Download"/>
</paths>
name表示生成URI時的別名,path是指相對路徑。
paths標簽下的子元素一共有以下幾種:
files-path 對應 Context.getFilesDir()
cache-path 對應 Context.getCacheDir()
external-path 對應 Environment.getExternalStorageDirectory()
external-files-path 對應 Context.getExternalFilesDir()
external-cache-path 對應 Context.getExternalCacheDir()
2.當然我們還需要修改打開apk文件的代碼
首先判斷下版本號,如果手機操作系統版本號大於等於7.0就通過FileProvider.getUriForFile方法生成一個Uri對象。
private void openAPK(String fileSavePath){
File file=new File(fileSavePath);
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri data;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//判斷版本大於等於7.0
// "com.ansen.fileprovider.fileprovider"即是在清單文件中配置的authorities
// 通過FileProvider創建一個content類型的Uri
data = FileProvider.getUriForFile(this, "com.ansen.fileprovider.fileprovider", file);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);// 給目標應用一個臨時授權
} else {
data = Uri.fromFile(file);
}
intent.setDataAndType(data, "application/vnd.android.package-archive");
startActivity(intent);
}