戲說(shuō)江湖靜如水,游蕩江湖才有情。我就是江湖中的一個(gè)戲子。
最近android N出來(lái)了,可是呢,原有的程序在androidN上運(yùn)行卻行不通了,最近樓主就遇到這樣的一個(gè)問(wèn)題。android.os.FileUriExposedException,網(wǎng)上一查,原來(lái)androidN加強(qiáng)了文件共享機(jī)制,當(dāng)傳遞file:///開(kāi)頭的uri時(shí)會(huì)拋出SecurityException。需要通過(guò)FileProvider來(lái)將File轉(zhuǎn)換為content://類型的uri。下面我們就來(lái)了解一下今天的主人公FileProvider吧。
what and why
FileProvider是ContentProvider的一個(gè)子類,不懂ContentProvider出門右拐上google,F(xiàn)ileProvider能夠很輕易的通過(guò)創(chuàng)建content://類型的uri而不是file:///類型的uri來(lái)分享文件,那么為什么通過(guò)content://類型uri就更安全呢,是因?yàn)榉窒韋ile:///類型的uri需要擁有文件系統(tǒng)權(quán)限,而這個(gè)文件系統(tǒng)的權(quán)限是針對(duì)任何應(yīng)用都可以申請(qǐng)的,那么你就可以對(duì)該文件進(jìn)行修改,而這種修改嚴(yán)格來(lái)說(shuō)是不安全的。而通過(guò)content://來(lái)分享,需要獲取臨時(shí)權(quán)限,而這種權(quán)限是跟隨接收的Activity或Service的生命周期來(lái)的。
How to use
那FileProvider要怎么使用呢?很簡(jiǎn)單只需要三步:
第一步:在AndroidMainfest.xml中聲明FileProvider
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="包名.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
authorities 一般是 包名+.fileprovider。resource是file_paths定義的是需要共享的文件夾路徑
第二步:定義file_paths
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_images" path="images/"/>
</paths>
該文件中files_path是共享文件夾路徑,在google中定義了幾個(gè)路徑如 下:
| 參數(shù) | 函數(shù) | 地址 |
|---|---|---|
| files-path | context.getFilesDir() | /data/user/0/包名/files |
| cache-path | context.getCacheDir() | data/user/0/包名/cache |
| external-path | Environment.getExternalStorageDirectory() | /storage/emulated/0 |
| external-files-path | context.getExternalFilesDir(null) | /storage/emulated/0/Android/data/包名/files |
| external-cache-path | Context.getExternalCacheDir() | storage/emulated/0/Android/data/包名/cache |
共享路徑就是(上述地址+path后面的參數(shù))所代表的實(shí)際地址。
第三步:獲取uri
在需要獲取uri的地方通過(guò)FileProvider獲取uri
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(context,authority,file);
} else {
uri = Uri.fromFile(file);
}
提醒
// 在傳遞Intent的時(shí)候一定要通過(guò)Intent的setFlags來(lái)給接收的應(yīng)用讀取權(quán)限哦
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
這里的authority就是我們?cè)诙xFileProvider中authority是一樣的,一般都是以包名+.fileprovider來(lái)定義,以避免和其他應(yīng)用定義的authority相混淆。
總結(jié)
通過(guò)這三步我們就獲得了我們需要的uri,一般的格式為content://包名.provider/path_name/file_name的形式。
這里path_name就是我們?cè)趂iles_path.xml中定義的name。走到這一步就大功告成了,你有沒(méi)有學(xué)會(huì)呢?