Android进阶之7.0适配-应用之间共享文件(FileProvider)

本文介绍如何使用FileProvider解决Android7.0以上版本中因file://Uri暴露引起的FileUriExposedException异常,包括FileProvider的配置、使用方法及其优点。

1 问题

(1)以下是一段简单的代码,它调用系统的相机app来拍摄照片:

void takePhoto() {
    Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    String cameraPhotoPath = getApplicationContext().getExternalCacheDir().getPath();
    File cameraFile = new File(cameraPhotoPath, "test.jpg");
    
    Uri photoUri = Uri.fromFile(cameraFile); // file://Uri
     
    takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); 
    startActivityForResult(takePhotoIntent, REQUEST_TAKE_PHOTO);
}

(2)在Android 7.0之前是没有任何问题,但是如果你尝试在7.0(API>=24)的系统上运行,会抛出文章开头提到的FileUriExposedException异常:

android.os.FileUriExposedException: 		
    file:///storage/emulated/0/DCIM/IMG_20170125_144112.jpg exposed beyond app through ClipData.Item.getUri()
    at android.os.StrictMode.onFileUriExposed(StrictMode.java:1799)
    at android.net.Uri.checkFileUriExposed(Uri.java:2346)
    at android.content.ClipData.prepareToLeaveProcess(ClipData.java:832)
    at android.content.Intent.prepareToLeaveProcess(Intent.java:8909)

2 原因

Android不再允许在app中把file://Uri暴露给其他app,包括但不局限于通过Intent或ClipData 等方法,原因在于使用file://Uri会有一些风险,比如:
①文件是私有的,接收file://Uri的app无法访问该文件;
②在Android6.0之后引入运行时权限,如果接收file://Uri的app没有申请READ_EXTERNAL_STORAGE权限,在读取文件时会引发崩溃。

因此,google提供了FileProvider,使用它可以生成content://Uri来替代file://Uri

3 解决方案

(1)首先在AndroidManifest.xml中添加provider

public class XstFileProvider extends FileProvider {
    public static final String XST_PROVIDER = ".XstFileProvider";

    /**
     * 获取文件Uri
     */
    public static Uri getUri(File file, Intent intent) {
        if (file == null || intent == null) {
            return null;
        }

        Uri uri;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            // 7.0及以上需要使用fileProvider方式
            uri = getUriForFile(BaseApplication.getContext(), getPackageName() + XST_PROVIDER, file);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        } else {
            uri = Uri.fromFile(file);
        }
        return uri;
    }
}
<provider
    android:name="com.utils.XstFileProvider"
    android:authorities="${applicationId}.XstFileProvider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/xst_file_paths" />
</provider>

(2)res/xml/xst_file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external_files" path="." />
</paths>

(3)修改代码

void takePhoto() {
    Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    String cameraPhotoPath = getApplicationContext().getExternalCacheDir().getPath();
    File cameraFile = new File(cameraPhotoPath, "test.jpg");
    
    Uri photoUri = FileProvider.getUri(cameraFile, takePhotoIntent); // content:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值