戏说江湖静如水,游荡江湖才有情。我就是江湖中的一个戏子。
最近android N出来了,可是呢,原有的程序在androidN上运行却行不通了,最近楼主就遇到这样的一个问题。android.os.FileUriExposedException,网上一查,原来androidN加强了文件共享机制,当传递file:///开头的uri时会抛出SecurityException。需要通过FileProvider来将File转换为content://类型的uri。下面我们就来了解一下今天的主人公FileProvider吧。
what and why
FileProvider是ContentProvider的一个子类,不懂ContentProvider出门右拐上google,FileProvider能够很轻易的通过创建content://类型的uri而不是file:///类型的uri来分享文件,那么为什么通过content://类型uri就更安全呢,是因为分享file:///类型的uri需要拥有文件系统权限,而这个文件系统的权限是针对任何应用都可以申请的,那么你就可以对该文件进行修改,而这种修改严格来说是不安全的。而通过content://来分享,需要获取临时权限,而这种权限是跟随接收的Activity或Service的生命周期来的。
How to use
那FileProvider要怎么使用呢?很简单只需要三步:
第一步:在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中定义了几个路径如 下:
参数 | 函数 | 地址 |
---|---|---|
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后面的参数)所代表的实际地址。
第三步:获取uri
在需要获取uri的地方通过FileProvider获取uri
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(context,authority,file);
} else {
uri = Uri.fromFile(file);
}
提醒
// 在传递Intent的时候一定要通过Intent的setFlags来给接收的应用读取权限哦
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
这里的authority就是我们在定义FileProvider中authority是一样的,一般都是以包名+.fileprovider来定义,以避免和其他应用定义的authority相混淆。
总结
通过这三步我们就获得了我们需要的uri,一般的格式为content://包名.provider/path_name/file_name的形式。
这里path_name就是我们在files_path.xml中定义的name。走到这一步就大功告成了,你有没有学会呢?