爲什麼用FileProvider
7.0和更高的版本使用FilrProvider,這是都知道的事情,但是爲什麼要使用FilrProvider,因爲Android 7.0不允許intent帶有file://的URI離開自身的應用了,要不然會拋出FileUriExposedException
想要在自己應用和其他應用之間共享File數據,只能使用content://的方式
(1)建議使用自己的FileProvider
建立自己的FileProvider,因爲一個程序中在使用FileProvider時候,可能你調用的第三方的裏面也用到了
FileProvider,那麼在使用的時候就會存在衝突,建議你在使用時候直接自定義一個:
public class MyFileProvider extends FileProvider {
}
(2)必須在清單文件中配置
在清單文件中註冊:
<provider
android:name=".bases.MyFileProvider"
android:authorities="包名.provider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities"
>
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/files_names"
tools:replace="android:resource"
/>
</provider>
/***android:name 它的值是固定的,是你的FileProvider,也可以用系統的自帶的
android:authorities:屬性的值必須要和代碼中使用的時候一樣。
<meat - data >標籤用來指定Uri的共享路徑。並且引用了一個@xml/file_paths資源
*/
###(3)
建立資源文件,對應於清單文件中配置的android:resource:屬性
在res - New - Directory - xml目錄 - New - File
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!--填寫你要所要申請訪問的目錄地址,name最好是你的目錄名,path是你要申請的目錄-->
<external-path name="camera_photos" path="." />
<external-path name="cache" path="Android/data/com.xuezj.fileproviderdemo/cache" />
<external-path name="images" path="Pictures/" />
<external-path name="dcim" path="DCIM/" />
//可以配置多個
</paths>
擴展 paths標籤 如何去完成
上述只是演示了外部存儲的一種,其實Google還提供以下幾種方法
(1)
<files-path name="name" path="path" /> 對應於getFilesDir()所表示的文件路徑
(2)
<cache-path name="name" path="path" /> 對應於getCacheDir()所表示的文件路徑
(3)
<external-path name="name" path="path" /> 對應於Environment.getExternalStorageDirectory(沒有參數)所表示的文件路徑,
(4)
<external-files-path name="name" path="path" />
對應於getExternalFilesDir(String) 或者getExternalFilesDir(null) 所表示的文件路徑
(5)
<external-cache-path name="name" path="path" /> 對應於getExternalCacheDir()表示的文件路徑
(6)
<external-media-path name="name" path="path" /> 對應於Context.getExternalMediaDirs()所表示的文件路徑
你可以選擇你想要的存儲位置,進行選擇,對於這些存儲位置不懂得,可以參考文件存儲體系
上面有關鍵兩個屬性,都有name 與 path,下面展示兩個例子
實例一:
路徑配置:
<paths>
<external-path name="my_images" path="Pictures" />
</paths>
使用:
File file = new File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),test.jpeg),;
Uri contentUri = FileProvider.getUriForFile(Main2Activity.this,"與清單文件中配置的android:authorities一樣",file);
intent.setDataAndType(contentUri, "application/vnd.android.package-archive"); //可以使用Uri了
前面提到的path = Pictures ,所以這裏使用的參數是Environment.DIRECTORY_PICTURES
前面如果是DCIM,這裏相應的也要變成這個Environment.DIRECTORY_DCIM
實例二:
路徑配置
<files-path name= "suiyidemingzi" path="apk_path" />
使用
private File apkpath = null;
private File contentFile = null;
apkpath = new File(getFilesDir(),"apk_path"); //先建立與路徑配置中你自定義的path一致的子目錄
,實例一通過傳參的方式得到的(因爲上述路徑是系統肯定自帶的)
contentFile = new File(apkpath,"test.jepg"); //這裏文件名無所謂了,隨便配置了
Uri contentUri = FileProvider.getUriForFile(Main2Activity.this, "和清單文件中配置的android:authorities一樣",contentFile );
這時候就得到7.0以上版本使用的Uri了
發現name可以隨便取,但是不能重複了,而path則要慎重用,和文件存儲中的內部存儲,外部存儲一一相關,作用是爲了抽象Uri中路徑,不讓外部應用直接得到使用.
上面的話如果難懂,下面是我自己的理解:
在xml中定義的標籤:
external-path 就等於 代碼中調用 Environment.getExternalStoragePublicDirectory這個方法得到的路徑,path就是子路徑
path表示的就是具體的哪個路徑如Pictures文件夾
兩者的作用一個樣,但是要對應起來,這樣系統在對外使用uri的時候纔不爲空。
files-path 就等於 getFilesDir()得到的路徑。
如果上面的話很難理解,不妨可以這樣想:
files-path + path 拼接成的路徑 必須保證和你代碼中構建的file路徑一致。
(4)
使用:在7.0之前的版本還是使用Uri.fromFile();方法的得到Uri
Uri uriP;
uriP = FileProvider.getUriForFile(MineStudentFragment.this.getContext()
, "包名.建議是自定義的provider"
, file);
/**這裏的參數二和清單文件註冊時候的android:authorities一致*/
(5)
權限,還是需要的。如果是想像外部存儲空間中存儲
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
錯誤:java.lang.IllegalArgumentException: Failed to find configured root that contains xxx文件路徑
再後來的使用中有些手機會返回這個異常,經過查找資料,發現需要額外配置如下:
<root-path
name = "root_path"
path="."
/>
這樣就可以解決部分機型因爲使用外置sd卡或者應用分身導致的問題。
華爲 p9與華爲 nove 2s在使用微信應用分身時候,會導致這個問題。
所以沒事就配置上就可以!!!!