最近做到一個用戶頭像裁剪上傳的功能,因為項目的target是7.0的削彬,所以遇到了一個應(yīng)用件共享文件的問題泣洞,總是報
android.os.FileUriExposedException:
這個錯誤忧风,然后查了一下資料,解決了問題球凰。
首先 我們要理解問題的根源所在:
對于面向 Android N 的應(yīng)用狮腿,Android 框架執(zhí)行的 StrictMode,API 禁止向您的應(yīng)用外公開 file://URI呕诉。如果一項包含文件 URI 的 Intent 離開您的應(yīng)用缘厢,應(yīng)用失敗,并出現(xiàn) FileUriExposedException異常义钉。
若要在應(yīng)用間共享文件昧绣,您應(yīng)發(fā)送一項 content://URI,并授予 URI 臨時訪問權(quán)限捶闸。進行此授權(quán)的最簡單方式是使用 FileProvider類夜畴。 如需有關(guān)權(quán)限和共享文件的更多信息,請參閱共享文件删壮。
FileProvider用在什么地方
A content URI allows you to grant read and write access using temporary access permissions. When you create an Intent containing a content URI, in order to send the content URI to a client app, you can also call Intent.setFlags() to add permissions. These permissions are available to the client app for as long as the stack for a receiving Activity is active. For an Intent going to a Service, the permissions are available as long as the Service is running.
使用臨時授權(quán)的方式允許讀寫content URI贪绘。當你創(chuàng)建一個Intent包含content URI,為了發(fā)送content URI給其他app央碟,使用Intent.setFlags()添加權(quán)限税灌。其他app的activity棧只要是活動的,就能訪問權(quán)限,service也一樣菱涤。
現(xiàn)在我們應(yīng)該知道問題原因了苞也,接下來開始使用FIleProvider解決問題
以我的項目中的問題為例
一、先在Manifest中定義一個FileProvider
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.nbeebank.licaishi.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/imagepaths" />
</provider>
name可以使用系統(tǒng)定義的 android.support.v4.content.FileProvider粘秆,也可重寫FileProvider methods如迟。
- android:authorities 字段指定了希望使用的Authority,該Authority針對于FileProvider所生成的content URI攻走。
authorities可以使用 包名 +"fileprovider"的方式命名殷勘。
<provider>下的<meta-data>指向了一個XML文件,該文件指定了我們希望共享的目錄路徑昔搂×嵯“android:resource”屬性字段是這個文件的路徑和名字(無“.xml”后綴)
接下定義一下上邊 resourse指定的共享文件的路徑
在res/xml/imagepaths.xml目錄下創(chuàng)建新文件(名字要和上邊的對應(yīng),因為上邊引用的路徑就是這個)摘符,內(nèi)容如下:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="images_external"
path="." />
該方式提供在外部存儲區(qū)域根目錄下的文件贤斜。它對應(yīng)Environment.getExternalStorageDirectory返回的路徑:
eg:”/storage/emulated/0”;
<files-path
name="images_files"
path="." />
該方式提供在應(yīng)用的內(nèi)部存儲區(qū)的文件/子目錄的文件。它對應(yīng)Context.getFilesDir返回的路徑:
eg:”/data/data/包名/files”议慰。
<cache-path
name="images_cache"
path="." />
該方式提供在應(yīng)用的內(nèi)部存儲區(qū)的緩存子目錄的文件蠢古。它對應(yīng)getCacheDir返回的路徑:
eg:“/data/data/包名/cache”;
<external-files-path
name="images_external_files"
path="." />
該方式提供在應(yīng)用的外部存儲區(qū)根目錄的下的文件别凹。它對應(yīng)Context#getExternalFilesDir(String) Context.getExternalFilesDir(null)返回的路徑草讶。
eg:”/storage/emulated/0/Android/data/包名/files”
<external-cache-path
name="images_external_cache"
path="." />
該方式提供在應(yīng)用的外部緩存區(qū)根目錄的文件。它對應(yīng)Context.getExternalCacheDir()返回的路徑炉菲。
eg:”/storage/emulated/0/Android/data/包名/cache”堕战。
<root-path
name="images_root"
path="." />
</paths>
我的主要問題,是root-path的問題拍霜,谷歌官方只簡單的提供了其他幾種方式的共享路徑嘱丢,但是對于手機根目錄沒有提及,所以我在測試華為機器的時候祠饺,每次路徑都跟其他的機型不一樣越驻,其他機型返回的都是正常的:
/storage/emulated/0/...
但是華為卻給我返回的是:
/storage/....
這就搞的我好尷尬
然后查閱了好多資料,發(fā)現(xiàn),這是因為,谷歌提及的方式對手機內(nèi)部存儲區(qū)文件共享是可以行的通的,但是對于外置SD卡是不行的,會報一個
IllegalArgumentException:Failed to find configured root that contains /storage/...
的錯誤.
查閱了好多資料,發(fā)現(xiàn)可以試著用root-path來解決讀取外置sd卡的問題
一個大神看了源碼后,發(fā)現(xiàn)root-path這個可以解決問題.(谷歌官方?jīng)]有提及)
root-path代表/也就是Android設(shè)備的根目錄,該目錄下包含著手機內(nèi)部存儲器道偷,外置SD卡等所有文件的目錄缀旁。
然后在我們需要的時候通過系統(tǒng)的getUriForFile(...)方法,獲取一個uri路徑即可
File file = new File(imagePath);
Uri photoURI = FileProvider.getUriForFile(activity, "com.nbeebank.licaishi.fileprovider", file);
我的項目里,需要打開相冊選擇照片和通過拍照獲取照片,所以,需要在兩個地方,都要這樣獲取uri
具體的例子,可以參考這兩個連接
http://www.reibang.com/p/3f9e3fc38eae