Android7.0以上通过FileProvider访问文件
//选择照片
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
String filename = "img_0001.png";
File file = new File(Environment.getExternalStorageDirectory(), filename);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
startActivityForResult(takePictureIntent, 0X000f);
}
此时如果我们使用Android 7.0或者以上的原生系统,再次运行一下,你会发现应用直接停止运行。
Caused by: android.os.FileUriExposedException:
file:///storage/emulated/0/img_0001.png
exposed beyond app through ClipData.Item.getUri()
at android.os.StrictMode.onFileUriExposed(StrictMode.java:1932)
at android.net.Uri.checkFileUriExposed(Uri.java:2348)
对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode API 政策禁止在您的应用外部公开 file:// URI。如果一项包含文件 URI 的 intent 离开您的应用,则应用出现故障,并出现 FileUriExposedException 异常。
FileProvider实际上是ContentProvider的一个子类,它的作用也比较明显了,file:///Uri不给用,那么换个Uri为content://来替代。
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.zhy.android7.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
注意一点,他需要设置一个meta-data,里面指向一个xml文件。
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<root-path name="root" path="" />
<files-path name="files" path="" />
<cache-path name="cache" path="" />
<external-path name="external" path="" />
<external-files-path name="name" path="path" />
<external-cache-path name="name" path="path" />
</paths>
在paths节点内部支持以下几个子节点,分别为:
1.<root-path/> 代表设备的根目录new File("/");
2.<files-path/> 代表context.getFilesDir()
3.<cache-path/> 代表context.getCacheDir()
4.<external-path/> 代表Environment.getExternalStorageDirectory()
5.<external-files-path>代表context.getExternalFilesDirs()
6.<external-cache-path>代表getExternalCacheDirs()
每个节点都支持两个属性:
1.name
2.path
path即为代表目录下的子目录,比如:
<external-path
name="external"
path="pics" />
代表的目录即为:Environment.getExternalStorageDirectory()/pics,其他同理。
本例使用的是SDCard所以这么写即可:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external" path="" />
</paths>
//选择照片
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
String filename = "img_0001.png";
File file = new File(Environment.getExternalStorageDirectory(), filename);
Uri fileUri = FileProvider.getUriForFile(this, "com.zhy.android7.fileprovider", file);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
startActivityForResult(takePictureIntent, 0X000f);
}
然后再看一眼我们生成的uri:
content://com.zhy.android7.fileprovider/external/20170601-041411.png
可以看到格式为:content://authorities/定义的name属性/文件的相对路径,即name隐藏了可存储的文件夹路径。