Android笔记:Android 7.0 FileUriExposedException 解决

本文介绍了解决Android应用中FileUriExposedException问题的两种方法:使用FileProvider创建content://格式的URI,及禁用VmPolicy检测。同时也针对某些特殊应用(如新浪微博)不支持content://格式URI的情况提供了额外解决方案。
摘要由CSDN通过智能技术生成

从Android 7.0开始,一个应用提供自身文件给其它应用使用时,如果给出一个file://格式的URI的话,应用会抛出FileUriExposedException。这是由于谷歌认为目标app可能不具有文件权限,会造成潜在的问题。所以让这一行为快速失败。详见这里。这里讨论两种解决方式。

1 FileProvider方式

这是谷歌官方推荐的解决方案。即使用FileProvider来生成一个content://格式的URI。具体实现方式如下:

manifest声明 

在manifest中声明一个provider。name(即类名)为android.support.v4.content.FileProvider。

<manifest>
    ... 
    <application>
        ...
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.mydomain.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
        ...
    </application>
</manifest>

其中authorities可以自定义。为了避免和其它app冲突,最好带上自己app的包名。file_paths.xml中编写该Provider对外提供文件的目录。文件放置在res/xml/下。 
2.编写file_paths.xml 
文件格式如下:

  • <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <files-path name="my_images" path="images/"/>
        ...
    </paths>

内部的element可以是files-path,cache-path,external-path,external-files-path,external-cache-path,分别对应Context.getFilesDir(),Context.getCacheDir(),Environment.getExternalStorageDirectory(),Context.getExternalFilesDir(),Context.getExternalCacheDir()等几个方法。后来翻看源码发现还有一个没有写进文档的,但是也可以使用的element,是root-path,直接对应文件系统根目录。不过既然没有写进文档中,其实还是有将来移除的可能的。使用的话需要注意一下风险。

3.在Java代码当中使用 
以分享一个图片为例:

  • File file = ...;    //要分享的图片文件
    Uri uri = FileProvider.getUriForFile(context, "com.mydomain.fileprovider", file);    //第二个参数是manifest中定义的`authorities`
    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.setType("image/*");
    intent.putExtra(Intent.EXTRA_TITLE, title);
    intent.putExtra(Intent.EXTRA_TEXT, text);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);    //这一步很重要。给目标应用一个临时的授权。
    startActivity(intent);    //或者其它最终处理方式


2 VmPolicy方式

以上方法固然是推荐使用的,正确的方法。但是我在实际开发中遇到这样的问题。某些应用(此处点名新浪微博)根本无法理解一个指向文件的content://格式的URI。新浪微博接收到这类URI之后,无法加载图片,并会在点击发送微博时崩溃。 
另一方面,新浪微博对权限管理的处理采取了一种比较流氓的方式。它会在启动时申请文件读写权限,而如果拒绝该权限的话,居然就直接退出了。我反正是不信什么需要文件权限来放缓存放数据的说辞。放缓存放数据有着一堆不需要权限的目录可用。但是这样一来,我们其实是不需要担心传递一个file://格式URI过去而对方没有权限的。 
话说回来,如何解决这一问题呢?我在调研的时候观察到严格模式的一个方法:StrictMode.VmPolicy.Builder.detectFileUriExposure()。顾名思义,调用这个方法就会检测FileUriExposure这件事。这个方法其实从API18就有了,是不是有可能在API24变成了默认选项呢? 
在Application.onCreate加入如下代码,置入一个不设防的VmPolicy:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
    StrictMode.setVmPolicy(builder.build());
}

再用旧的方式直接把file://格式的URI发送出去。居然成功了,没有再抛出FileUriExposedException。

3 小结 
个人感觉第二个比较好用! 
最终我采取的综合方案是,先使用PackageManager.checkPermission检测对方的app有没有取得文件读写权限。如果有的话,给对方发送file://格式URI。如果没有的话,给对方发送FileProvider生成的URI并临时授权。 
原本一个有标准解决方案的问题,因为某些应用不遵循规范而需要做更多的调研和workaround。实在是麻烦。希望可以帮助到遇到同样问题的人。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值