Android7.0以上通过FileProvider访问文件

2020-05-20  本文已影响0人  狂奔的_土豆
        //选择照片
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隐藏了可存储的文件夹路径。

上一篇下一篇

猜你喜欢

热点阅读