Android N行为变更 解决Android N上报android.os.FileUriExposedException

记得之前将项目的targetSdkVersion改成26之后,在Android 8.0的设备上报android.os.FileUriExposedException。今天想起这事,并且有时间,便将解决这个问题的整个过程记录下来。

问题/现象
将项目的targetSdkVersion改成26之后,在Android 8.0的华为荣耀8手机上报android.os.FileUriExposedException。经过排查,定位到是在执行以下代码的时候报错:

                    Intent intent = new Intent(Intent.ACTION_SEND);
                    intent.setPackage("com.alibaba.android.rimet");
                    intent.setType("application/pdf");
                    intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(path)));
                    activity.startActivity(intent);

原因
Android N(Android 7.0)行为变更,对于面向Android 7.0的应用,Android框架执行的StrictMode API政策禁止在应用外部公开 file://URI。
我们将targetSdkVersion改成了26,又是在Android 8.0的设备上运行,所以报了android.os.FileUriExposedException。

解决方案
要在应用间共享文件,应发送一项 content://URI,并授予URI临时访问权限。进行此授权的最简单方式是使用 FileProvider 类。
具体解决步骤如下:
1、新建一个CustomFileProvider,继承FileProvider。
这里简单介绍下FileProvider,FileProvider是ContentProvider的子类,
在这里插入图片描述
2、在AndroidManifest.xml中声明:

        <provider
            android:authorities="com.example.zdj.content_provider.CustomFileProvider"
            android:name="android.support.v4.content.FileProvider"
            android:grantUriPermissions="true"
            android:exported="false">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"/>
        </provider>

并且在res下新建一个xml文件夹,然后在该xml文件夹下新建一个file_paths.xml,内容如下:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="files_path" path="SHIZAI_PDF"/>
</paths>

这里我们做一些说明:
android:authorities 用来标识provider的唯一标识
android:name 固定为android.support.v4.content.FileProvider
android:grantUriPermissions 用来控制共享文件的访问权限
android:exported 设置为false

那么为什么要有这个

<meta-data
     android:name="android.support.FILE_PROVIDER_PATHS"
     android:resource="@xml/file_paths"/>

呢?
因为我们要指定路径和转换规则。
android:name=“android.support.FILE_PROVIDER_PATHS"这个是固定的,
android:resource=”@xml/file_paths"这里的file_paths就是对应我们刚刚新建的file_paths.xml。
那么我们为什么需要这样一个xml文件呢?
因为我们要使用content://uri替代file://uri,那么content://uri如何定义呢?我们需要一个虚拟的路径对文件路径进行映射,所以需要编写一个xml文件,通过path以及xml节点确定可访问的目录,通过name属性来映射真实的文件路径。

3、1、2两步都做完了之后,我们就可以直接使用FileProvider了。我们需要注意一点,那就是我们要检查系统版本,因为只有在Android N以上,我们才会去使用这种方式。下面我们直接上代码:

                    Intent intent = new Intent(Intent.ACTION_SEND);
                    intent.setPackage("com.alibaba.android.rimet");
                    intent.setType("application/pdf");
                    if (Build.VERSION.SDK_INT >= 24) {
                        intent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(activity, "com.example.zdj.content_provider.CustomFileProvider", new File(path)));
                    } else {
                        intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(path)));
                    }
                    activity.startActivity(intent);

猜你喜欢

转载自blog.csdn.net/zdj_Develop/article/details/88579427
今日推荐