開發中經常需要將某個檔案向另一個應用程式傳遞,如圖片上傳到另一個應用程式、檔案在不同儲存路徑之間的複製貼上等都需要共享檔案,可以這樣理解接收檔案的應用程式是在向提供檔案的應用程式傳送請求。
從 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 以上愉快的共享檔案了。