使用FileProvider 分享文件

起因

我们的截图应用在Android O 上使用分享功能的时候crash了,错误关键词:FileUriExposedException
Google 一下发现这个问题从Android N 开始出现的,当你给使用 file:/// Uri 分享文件的时候会抛出这个异常
但是奇怪的是我们在Android N 上使用分享功能的时候并没有出现问题,不管怎样有问题就要解决。
一句话概括,我们要做的就是使用 content:// Uri 代替 file:/// Uri

解决过程

报错代码:

    public void shareScreenshot() {
        final File imageFile = new File(mScreenshotPath);
        // Create a shareScreenshot intent
        Intent sharingIntent = new Intent(Intent.ACTION_SEND);
        sharingIntent.setType("image/png");
        sharingIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(imageFile));
        startActivity(Intent.createChooser(sharingIntent, getResources().getText(R.string.share)));
    }

当我们使用Uri.fromFile(imageFile)的时候获得了 file:/// Uri,就是使用这个 file:/// Uri 的时候报的错。
下面我们分几步把file:/// Uri 替换成 content:// Uri

  1. 指定FileProvider
    在AndroidManifest.xml 中定义FileProvider。
    关注两个值,android:authoritiesandroid:resource,前者是包名跟.fileprovider, 后者是定义你想要访问的目录的文件,下面会细说。

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.example.myapp">
       <application
           ...>
           <provider
               android:name="android.support.v4.content.FileProvider"
               android:authorities="com.ckt.screenshot.provider"
               android:grantUriPermissions="true"
               android:exported="false">
               <meta-data
                   android:name="android.support.FILE_PROVIDER_PATHS"
                   android:resource="@xml/provider_paths"/>
           </provider>
           ...
       </application>
    </manifest>
  2. 指定可共享的目录
    在res/xml/创建和上面android:resource 对应的文件provider_paths.xml。

    <paths>
       <files-path path="images/" name="myimages" /> <!--仅作举例-->
       <external-path name="external_files" path="Pictures/Screenshots/"/>
    </paths>

    在这个例子中files-path 标签代表了应用内部存储files/子目录下的文件, external-path 标签代表外置存储根目录。其他标签请查看FileProvider 介绍。

    • path 代表的是子目录。它是标签代表的目录的子目录。比如上例中的两个目录,第一个是应用内部存储files/下的images/ 子目录,另一个是存储卡根目录下的Pictures/Screenshots/ 子目录。
      注意: 不能用指定文件名的方式分享特定文件,也不能用通配符的方式指定一些文件。
    • name 是content Uri 中的一段,它可以说是path的别名,为了加强安全性,用name的值代替实际的path 路径添加到content Uri中;
  3. 修改java 代码
    使用FileProvider.getUriForFile 获得content Uri。

    +        Uri imageUri = FileProvider.getUriForFile(mContext,
    +                BuildConfig.APPLICATION_ID + ".provider",
    +                imageFile);
            // Create a shareScreenshot intent
            Intent sharingIntent = new Intent(Intent.ACTION_SEND);
            sharingIntent.setType("image/png");
    -        sharingIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(imageFile));
    +        sharingIntent.putExtra(Intent.EXTRA_STREAM, imageUri);

    实际文件路径 imageFile /storage/emulated/0/Pictures/Screenshots/Screenshot_20180224-104038.png
    而我们获得 imageUri content://com.ckt.screenshot.provider/external_files/Screenshot_20180224-104038.png

    结合provider_paths.xml 对比可以看出Pictures/Screenshots(path 实际路径)替换成了external_files(name 别名)。

不使用Uri.fromFile() 的几点理由

  • Does not allow file sharing across profiles.
  • 要求分享文件的应用有WRITE_EXTERNAL_STORAGE 的权限(Android 4.4 及以下)
  • 要求接收文件的应用有 READ_EXTERNAL_STORAGE的权限,分享文件给没有这些权限的应用会失败,比如Gmail就没有这个权限。
    最重要的一点,在Android N及以上会出现Crash。

参考连接

  1. FileUriExposedException
  2. FileProvider
  3. Setting Up File Sharing
  4. android.os.FileUriExposedException: file:///storage/emulated/0/test.txt exposed beyond app through Intent.getData()
  5. 解决 Android N 7.0 上 报错:android.os.FileUriExposedException
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值