FileProvider解决FileUriExposedException

本文介绍在Android 7.0及以上版本中遇到FileUriExposedException的原因及解决方法。通过使用FileProvider生成content://格式的URI,确保应用间安全地传递文件。

FileUriExposedException

在给app做版本升级的时候,先从服务器下载新版本的apk文件到sdcard路径,然后调用安装apk的代码,一般写法如下:

private void openAPK(String fileSavePath){
    File file=new File(fileSavePath);
    Intent intent = new Intent(Intent.ACTION_VIEW);
    Uri data = Uri.fromFile(file);
    intent.setDataAndType(data, "application/vnd.android.package-archive");
    startActivity(intent);
}

这样的写法在Android7.0版本之前是没有任何问题,只要给一个apk文件路径就能打开安装。但是在Android7.0版本上会报错:

 android.os.FileUriExposedException:
 file:///storage/emulated/0/Download/FileProvider.apk 
 exposed beyond app through Intent.getData()

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

FileProvider方式解决

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

1.在Manifest.xml中声明一个provider。
<application ···>
        ···
    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.ansen.fileprovider.fileprovider"
        android:grantUriPermissions="true"
        android:exported="false">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
</application>

android:name值是固定的,android:authorities随便写但是必须得保证唯一性,我这边用的是包名+”fileprovider”,android:grantUriPermission跟android:exported固定值。

里面包含一个meta-data标签,这个标签的name属性固定写法,android:resource对应的是一个xml文件。我们在res文件夹下新建一个xml文件夹,在xml文件夹下新建file_paths.xml文件。内容如下:

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

name表示生成URI时的别名,path是指相对路径。

paths标签下的子元素一共有以下几种:

files-path 对应  Context.getFilesDir()
cache-path 对应  Context.getCacheDir()
external-path 对应 Environment.getExternalStorageDirectory()
external-files-path 对应  Context.getExternalFilesDir()
external-cache-path 对应  Context.getExternalCacheDir()
2.当然我们还需要修改打开apk文件的代码

首先判断下版本号,如果手机操作系统版本号大于等于7.0就通过FileProvider.getUriForFile方法生成一个Uri对象。

private void openAPK(String fileSavePath){
    File file=new File(fileSavePath);
    Intent intent = new Intent(Intent.ACTION_VIEW);
    Uri data;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//判断版本大于等于7.0
        // "com.ansen.fileprovider.fileprovider"即是在清单文件中配置的authorities
        // 通过FileProvider创建一个content类型的Uri
        data = FileProvider.getUriForFile(this, "com.ansen.fileprovider.fileprovider", file);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);// 给目标应用一个临时授权
    } else {
        data = Uri.fromFile(file);
    }
    intent.setDataAndType(data, "application/vnd.android.package-archive");
    startActivity(intent);
}

源码下载

如果你想第一时间看我的后期文章,扫码关注公众号,每周不定期推送Android开发实战教程文章…

      Android开发666 - 安卓开发技术分享
             扫描二维码加关注

Android开发666

<think>好的,我现在需要回答用户关于Android中FileProvider的作用和应用场景的问题。根据用户提供的引用和之前的回答,我需要综合信息,给出一个结构清晰的解释。 首先,用户之前已经询问过FileProvider的源码解析,所以我需要避免重复,转而侧重于作用和场景。用户提供的引用提到了几个关键点: 1. FileProvider继承自ContentProvider,用于安全共享文件,替代file:// URI。 2. 从Android 7.0开始,禁止使用file:// URI,必须使用FileProvider来避免FileUriExposedException。 3. 应用场景包括调用相机保存照片和安装APK文件。 在之前的回答中,我已经详细解析了FileProvider的源码,包括初始化、路径映射、URI生成和安全机制。现在需要将这些技术细节简化为作用和应用场景,让用户更容易理解。 首先,作用方面:FileProvider的主要作用是通过content:// URI安全共享文件,解决Android 7.0及以上版本的文件权限问题。它通过ContentProvider机制,提供临时访问权限,防止其他应用直接访问文件路径。 应用场景方面,用户提到的相机和安装APK是常见例子。需要补充更多场景,比如分享文件到其他应用,或者跨应用的文件访问。例如,用户的应用生成一个PDF文件,想要通过电子邮件发送,这时候也需要使用FileProvider。 另外,需要提到配置FileProvider的步骤,比如在AndroidManifest中声明,配置XML路径。但用户的问题是关于作用和应用场景,所以这部分可能简要提及即可。 还要注意用户引用的内容,比如引用3中的代码示例,展示了如何在Intent中使用FileProvider,并添加临时权限标志。这可以作为一个应用场景的实例。 需要确保回答结构清晰,分点说明,并包含实际例子,帮助用户理解。同时,要引用用户提供的引用资料,比如在安全机制部分引用[^2]和[^3]。 可能的问题点:是否覆盖了所有常见应用场景?是否需要提到FileProvider的替代方案或比较?不过用户的问题集中于FileProvider本身,不需要扩展其他内容。 总结:回答需要分两部分,作用和应用场景,每部分用清晰的标题和条目,结合引用中的信息和之前的源码解析,用简洁的语言解释,并给出实际例子。</think>### Android FileProvider 作用及常见应用场景解析 --- #### 一、核心作用 1. **安全文件共享** FileProvider 是 `ContentProvider` 的子类,通过 **`content://` URI** 代替传统的 `file://` URI,解决 Android 7.0(API 24)及以上版本因 `StrictMode` 策略导致的 `FileUriExposedException` 问题[^2]。 - 传统方式:直接暴露物理路径,存在安全风险 - FileProvider:通过权限控制,限制其他应用仅能访问指定文件 2. **权限动态管理** - 提供临时读写权限(通过 `Intent.FLAG_GRANT_READ_URI_PERMISSION`),避免长期授权风险[^3] - 可精确控制可共享的目录范围(通过 XML 路径配置) 3. **跨应用通信桥梁** 作为系统级内容提供者,封装文件访问细节,实现应用间安全数据传递。 --- #### 二、应用场景详解 1. **调用系统相机并保存照片** **传统问题**:直接指定 `file://` 路径保存照片会导致 Android 7.0+ 崩溃。 **FileProvider 方案**: ```java // 指定照片存储路径 File photoFile = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "photo.jpg"); Uri photoUri = FileProvider.getUriForFile(context, AUTHORITY, photoFile); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // 授予相机应用临时写权限 startActivityForResult(intent, REQUEST_CODE); ``` 2. **安装APK文件** **Android 8.0+ 限制**:非市场类应用安装需通过 `content://` URI 传递 APK。 **实现代码**: ```java File apkFile = new File(getExternalFilesDir("apk"), "app_update.apk"); Uri apkUri = FileProvider.getUriForFile(context, AUTHORITY, apkFile); Intent installIntent = new Intent(Intent.ACTION_INSTALL_PACKAGE); installIntent.setData(apkUri); installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(installIntent); ``` 3. **跨应用文件分享** **示例**:将应用内生成的 PDF 文件分享到邮件客户端 ```java File pdfFile = new File(getFilesDir(), "report.pdf"); Uri pdfUri = FileProvider.getUriForFile(context, AUTHORITY, pdfFile); Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("application/pdf"); shareIntent.putExtra(Intent.EXTRA_STREAM, pdfUri); shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(Intent.createChooser(shareIntent, "Share PDF")); ``` 4. **访问其他应用私有文件** 通过定义共享路径,允许特定应用访问本应用的私有目录文件(如合作应用间数据交换)。 --- #### 三、配置要点(补充) 1. **AndroidManifest 声明** ```xml <provider android:name="androidx.core.content.FileProvider" android:authorities="com.example.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> ``` 2. **路径配置文件 (res/xml/file_paths.xml)** ```xml <paths> <!-- 对应 Context.getFilesDir() --> <files-path name="internal_files" path="documents/"/> <!-- 对应外部存储根目录 --> <external-path name="external_storage" path=""/> </paths> ``` --- #### 四、与其他方案的对比 | 方案 | 安全性 | 兼容性(Android 7.0+) | 灵活性 | |--------------------|-------|-----------------------|-------| | 传统 `file://` URI | 低 | 崩溃 | 高 | | FileProvider | 高 | 完美支持 | 中 | | `ContentResolver` | 高 | 支持 | 低 | --- #### 五、典型问题规避 1. **权限未授予** 确保 Intent 中添加 `FLAG_GRANT_READ_URI_PERMISSION` 或 `FLAG_GRANT_WRITE_URI_PERMISSION`[^3]。 2. **路径配置错误** 检查 XML 中 `<path>` 标签是否覆盖目标文件的实际存储路径。 3. **多进程初始化冲突** 继承 FileProvider 自定义子类,在 `onCreate()` 中加同步锁(详见源码解析部分)。 --- **#总结** FileProvider 是 Android 安全体系中的重要组件,通过权限隔离和路径映射机制,在保障文件访问安全的前提下,为相机调用、应用安装、跨应用文件分享等场景提供标准化支持[^1][^2]。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安辉就是我

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值