FileProvider是什麼?
FileProvider
是安卓7.0以上版本因爲谷歌官方對StrictMode
(嚴格模式)的開啓而產生的安全機制的產物(也就是說,在7.0以下是不需要它的),它繼承自ContentProvider
。
那安卓7.0以上版本我就一定需要它嗎?
不是。只有在你需要把文件在2個(或以上)app之間交互的時候才需要它。
有人說我也沒打算把我的app的數據給別的app或者需要別的app給我數據呀?其實不然,不一定其他app是指第三方app,還包括系統app,比如:系統相機拍照(讓系統相機把照片保存到你指定的路徑)?要不再把照片給裁剪一下(uCrop
中的UCrop.of(xxx).start()
方法其實也是用startActivityForResult(xxx)
方法)?檢測到新版本時候再自動安裝一下(把需要安裝的apk包給系統安裝器來安裝)?
總的來說就一句話:只要你需要把文件地址(比如:file://xxx/xx
)用Intent
傳遞的時候就必須用它。
怎麼用?
簡單來說就是把file://xxx/xx
這種File格式地址改造爲Uri
格式地址content://xxx/xx
。
那怎麼改呢?很簡單:Uri uri = FileProvider.getUriForFile(context,authority,file);
,第2個參數待會兒再講,第3個參數就是需要被改造的File文件,這個Uri的路徑格式就是上面提到的content://xxx/xx
。
但是,在使用這個方法之前還有2個前提步驟需要先做:
- 前面提到過,
FileProvider
繼承自ContentProvider
,所以自然需要在AndroidManifest.xml
裏先聲明它:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.my_test_file_provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/my_file_paths">
</meta-data>
</provider>
簡單作一下解釋:
android:name
:實際解析處理這個provider
的FileProvider
,這個一般就用系統幫我實現的androidx.core.content.FileProvider
。不一定非要androidx
裏的,v4
包也有這種FileProvider
。當然,你也可以自己實現一個。
android:authorities
:前面提到的.getUriForFile(xxx,authority,xxx)
就是它。作用就是用它來匹配找到對應該使用哪個provider
的。值得注意的是,這個值在一個app中必須是唯一的,所以最好根據${applicationId}
來讓gradle
來自動生成。不過也不是一定要這麼搞,如果這個provider
本身就是app
裏使用,並不是作爲一個module
或lib
來使用的話,你也可以寫死。(它本質是一個字符串而已,但是很多博文都是${applicationId}.fileProvider
,其實大可不必,你隨便寫一個都行,只要在上面那個方法的第二個字段跟它一樣就行。)
android:exported
:是否需要公開這個provider
? 一般我們單進程幹活的app都設置爲false就行
android:grantUriPermissions
:是否授權這個文件給第三方。更多信息可以自行了解一下Context.grantUriPermission(toPackage,uri,modeFlags)
這個方法
meta-data
裏的android:name
:也是固定值不用動,源碼裏就是寫死的用這個值來解析xml的
meta-data
裏的android:resource
:需要自己新建一個文件res/xml/my_file_paths.xml
,路徑與文件名就是這個值。
- 在自己新建的
res/xml/my_file_paths.xml
裏寫下如下代碼:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="xxx" path="xxxx" />
</paths>
paths
裏值可以是如下幾個:
<root-path name="xxx" path="xxxx" /> <!-- 對應/跟目錄 -->
<files-path name="xxx" path="xxxx" /> <!-- 對應context.getFilesDir()獲取到的目錄-->
<cache-path name="xxx" path="xxxx" /> <!-- 對應context.getCacheDir()獲取到的目錄 -->
<external-path name="xxx" path="xxxx" /> <!-- 對應Environment.getExternalStorageDirectory()獲取到的目錄 -->
<external-files-path name="xxx" path="xxxx" /> <!-- 對應context.getExternalFilesDir(type)獲取到的目錄,這個type其實就是拼接在當前這個方法獲取到的路徑的下一級。比如:/storage/emulated/0/Android/data/com.example.myapp/files/type -->
<external-cache-path name="xxx" path="xxxx" /> <!-- 對應context.getExternalCacheDir(type)獲取到的目錄 -->
<external-media-path name="xxx" path="xxxx" /> <!-- 對應context.getExternalMediaDirs()[0]獲取到的目錄 -->
上面的name
裏的xxx
是用作生成的Uri
格式路徑的中間路徑,比如:content://com.example.myapp/xxx/test1.jpg
上面的path
裏的xxxx
是用作生成File
格式路徑的中間路徑,比如:
/storage/emulated/0/Android/data/com.example.myapp/files/xxxx/test1.jpg
上面每個值後面的註釋就是上面這個File
格式對應的,比如:external-files-path
對應的context.getExternalFilesDir(type)
獲取到的值----/storage/emulated/0/Android/data/com.example.myapp/files
也就是說,當需要傳遞某個文件時,你可以用以上context.getXxxDir()
方法的路徑來選擇具體在paths.xml
裏寫對應哪個值。還是最開始講的那句:其目的無非就是把File
的路徑轉爲Uri
來暴露給其他app
。
好了,至此講得差不多。