FileProvider
FileProvider 這個組件在Android 22.0.0 (也就是 Android 5.0 ) 版本下加入進Android系統,該組件是ContentProvider的子類,功能就是用來提供文件在跨進程間的訪問能力。大家千萬不要覺得是Android 7.0 才加入進來的。
為什么Android 7.0 文件共享要使用FileProvider
這個原因是因為,Android 7.0 版本以后,對於StrictMode的審查又嚴格了一步,就如在Android 3.0以后加入的NetWorkOnMainThread的異常一樣,Google對於會對Android系統造成體驗或者安全問題的部分會使用嚴格模式來限制程序開發者。
對於文件訪問在Android 7.0以前可以使用file://uri的方式訪問,但是這個地方有個問題,就是即使不是你自身應用產生的文件,只要知道對方的uri則就可以調用到,這樣在安全性上就產生了風險。所以Android 7.0后新增了對文件跨進程訪問的限制,這個限制會造成,如果使用file://uri的方式訪問,則會出現android.os.FileUriExposedException的異常。
FileProvider 使用
Step 1 AndroidManifest ,注冊FileProvider
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application ...> <provider android:name="android.support.v4.content.FileProvider" android:authorities="org.unreal.update" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/update_files" /> </provider> ... </application> </manifest>
需要注意的地方是
android:authorities 參數,如同ContentProvider,此處需要你提供一個uri的authorities,以便於content://android:authorities/uri訪問到,android:authorities可以隨意定義
Step 2 res下創建xml文件夾,並在xml文件夾下創建update_files.xml
<resources xmlns:android="http://schemas.android.com/apk/res/android"> <paths> <files-path path="files" name="files" /> <cache-path path="files" name="cache" /> <external-path path="files" name="external" /> <external-files-path path="files" name="externalfiles"/> <!-- 此標簽需要 support 25.0.0以上才可以使用--> <external-cache-path path="files" name="externalcache"/> </paths> </resources>
name:名稱標志字符串,不可以同名!
path:文件夾“相對路徑”,完整路徑取決於當前的標簽類型。
標簽 | 路徑 |
---|---|
…….. | * 代表 當前文件夾及其子文件夾 |
file-path | 物理路徑為Context.getFilesDir() + /files/* |
cache-path | 物理路徑為Context.getCacheDir() + /files/* |
external-path | 物理路徑為Environment.getExternalStorageDirectory() + /files/* |
external-files-path | 物理路徑為Context.getExternalFilesDir(String) + /files/* |
external-cache-path | 物理路徑為Context.getExternalCacheDir() + /files/* |
在簡書看到 zhuhf 大神的關於FileProvider的文章后,發現還有一個隱藏的標簽
文章地址: http://www.jianshu.com/p/55eae30d133c
<root-path name="name" path="path" />
標簽 | 路徑 |
---|---|
…….. | * 代表 當前文件夾及其子文件夾 |
root-path | 物理路徑相當於 /path/* |
Step 3 通過getUriForFile獲取到uri
data = FileProvider.getUriForFile(context,"之前在AndroidManifest中配置的android:authorities", "7.0后文件的路徑"); // 給目標應用一個臨時授權 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
FLAG_GRANT_READ_URI_PERMISSION:表示讀取權限;
FLAG_GRANT_WRITE_URI_PERMISSION:表示寫入權限;
根據你的需求,是讀取呢,還是寫入自行選擇
例子 安裝APK
private void installApk(File apk) { if (!apk.exists()) { return; } Uri data; Intent intent = new Intent(Intent.ACTION_VIEW); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // UpdateConfig.FILE_PROVIDER_AUTH 即是在清單文件中配置的authorities data = FileProvider.getUriForFile(context, UpdateConfig.FILE_PROVIDER_AUTH, apk); // 給目標應用一個臨時授權 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } else { data = Uri.fromFile(apk); } intent.setDataAndType(data, "application/vnd.android.package-archive"); context.startActivity(intent); android.os.Process.killProcess(android.os.Process.myPid()); }