Android中的FileProvider粗解

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個前提步驟需要先做:

  1. 前面提到過,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:實際解析處理這個providerFileProvider,這個一般就用系統幫我實現的androidx.core.content.FileProvider。不一定非要androidx裏的,v4包也有這種FileProvider。當然,你也可以自己實現一個。
android:authorities:前面提到的.getUriForFile(xxx,authority,xxx)就是它。作用就是用它來匹配找到對應該使用哪個provider的。值得注意的是,這個值在一個app中必須是唯一的,所以最好根據${applicationId}來讓gradle來自動生成。不過也不是一定要這麼搞,如果這個provider本身就是app裏使用,並不是作爲一個modulelib來使用的話,你也可以寫死。(它本質是一個字符串而已,但是很多博文都是${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,路徑與文件名就是這個值。

  1. 在自己新建的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

好了,至此講得差不多。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章