原文首發於微信公眾號:躬行之,歡迎關注交流!
開發中經常需要將某個檔案向另一個應用程式傳遞,如圖片上傳到另一個應用程式、檔案在不同儲存路徑之間的複製貼上等都需要共享檔案,可以這樣理解接收檔案的應用是在向提供檔案的應用傳送請求。
從 Android 7.0 開始,Android 執行 StrictMode 策略,禁止在應用外部公開 file://URL,如果在 Android 7.0 以上的應用不使用 FileProvider ,則會丟擲 FileUriExposedException 異常,Android 7.0 以後要在應用之間共享檔案要使用 content://URL 授予 URL 臨時訪問許可權,即要使用 FileProvider 的方式來授予臨時訪問許可權,具有臨時訪問許可權的 URL 是安全的,這種臨時的 URL 會自動過期,其中 FileProvider 提供的 getUriForFile() 用於生成檔案的內容。
在所有情況下,從您的應用程式向另一個應用程式提供檔案的唯一安全方法是向接收應用程式傳送檔案的內容URI,並授予該URI的臨時訪問許可權。具有臨時URI訪問許可權的內容URI是安全的,因為它們僅適用於接收URI的應用程式,並且它們會自動過期。 Android FileProvider元件提供getUriForFile()方法,用於生成檔案的內容URI。
這裡也會提到一個在 Android 7.0 及更高版本時經常出現的異常:FileUriExposedException,通過使用 FileProvider 就可以解決該異常,當然這也是 Android 系統在安全性上不斷完善的結果。
- 指定FileProvider
- 指定檔案共享路徑
指定FileProvider
在 AndroidManifest 檔案中指定 Provider,參考如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<application
...>
<!--android:authorities="${applicationId}.yourname"-->
<provider
android:name="android.support.v4.content.FileProvider"
<!--authorities屬性指定要用於FileProvider生成的內容URI的URI許可權,一般是applicationId.yourname"組成-->
android:authorities="com.example.myapp.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
...
</application>
</manifest>
指定檔案共享路徑
上面程式碼中在 meta-data 目錄中指定了要共享的檔案目錄,檔案目錄在 filepathd.xml 中定義,可在相應的 xml 中定義的路徑有以下幾種,具體參考如下:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<paths>
<!--表示裝置的根目錄(new File("/"))-->
<root-path name="root" path="" />
<!--表示context.getFileDir()-->
<files-path name="files" path="" />
<!--表示context.getCacheDir()-->
<cache-path name="cache" path="" />
<!--表示Environment.getExternalStorageDirectory()-->
<external-path name="external" path="" />
<!--表示context.getExternalFilesDirs()-->
<external-files-path name="name" path="path" />
<!--表示getExternalCacheDirs()-->
<external-cache-path name="name" path="path" />
</paths>
</resources>
在 xml 中表示某個路徑需要兩個屬性,path 表示當前指定目錄的子目錄,如果不指定則表示的是當前指定目錄下的根目錄及子目錄,name 表示會將 name 新增的 URL 後面作為該檔案的訪問路徑,參考如下:
//表示當前要共享的檔案會在 context.getFileDir() 目錄下的 images 子目錄下查詢要共享的檔案
<paths>
<files-path path="images/" name="myImage" />
</paths>
//表示最終生成的共享的檔案URL
content://com.example.myapp.fileprovider/myImage/image.jpg
獲取Uri
最後,配置完成之後,在所有需要使用檔案相關的,在獲取 Url 時應該按照如下方式獲取,具體如下:
public Uri getUri(File file) {
Uri uri = null;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(mContext, mContext.getPackageName() + ".youName", file);
} else {
uri = Uri.fromFile(file);
}
return uri;
}
這樣就可以在 Android 7.0 以上愉快的共享檔案了,這個知識點也算是開發中經常會遇到到。