聊聊对Andorid的FileProvider的理解

最近在项目中遇到一个有关FileProvider的问题,由此来学习了一下FileProvider,有关笔记记录如下。

FileProvider是 Android 系统中一个特殊的内容提供者(ContentProvider), 它主要用于应用之间安全的共享文件。通过 FileProvider,应用可以生成一个 content URI并授予其它应用临时访问的权限,而不需要将文件的实际路径暴露给其它应用。这样可以有效的避免安全问题。

主要用途

FileProvider主要应用于以下几种场景:

  1. 文件共享:当一个应用需要与另外一个应用共享文件时(比如使用Intent发送图片/文件),可以使用 FileProvider来生成一个 content URI;
  2. 文件访问控制:通过 FileProvider 可以控制其他应用对文件的读写权限,确保文件访问的安全性;
  3. 避免使用file://URI:由于从Andorid 7.0起,file://URI被弃用,使用 FileProvider 提供的 content URI 可以避免兼容性。
使用步骤

a. 在 AndroidManifest.xml 中声明 FileProvider:

xml 复制代码
<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

b. 配置文件路径

res/xml 目录下创建一个 file_paths.xml 文件,定义共享文件的路径。

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path name="my_files" path="." />
</paths>

c. 生成 content URI

使用 FileProvider.getUriForFile() 方法来生成一个 content URI。

java 复制代码
File file = new File(context.getExternalFilesDir(null), "example.jpg");
Uri contentUri = FileProvider.getUriForFile(context, "com.example.myapp.fileprovider", file);

d. 授予临时权限,并分享

当通过Intent发送文件时,可以授予接收临时访问文件的权限:

java 复制代码
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_STREAM, contentUri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

startActivity(Intent.createChooser(intent, "Share image via"));

详细解释 file_paths.xml

file_paths.xml, <external-files-path name="my_files" path="." />的解释如下:

  • external-files-path 用于指定应用的外部私有存储目录中的文件路径。外部私有存储目录是指通过 Context.getExternalFilesDir()方法获取的目录,它通常用于存储应用专属的文件,这些文件会随着应用的卸载而被删除。
  • name="my_files": 这是一个可选的属性,用于为这个路径配置起一个别名。在生成的Content URI中,这个别名会作为路径的一部分。如果不需要这个别名,那么也可以直接去掉。
  • path="."这个属性指定了相对于 external-files-path 目录的相对路径. "."代表当前目录,即整个 external-files-path 所在的目录。如果你想指定该目录下的某个子目录,你可以在 path属性中提供相对路劲,例如path="images/".

我们来一个例子:

假设我们的包名是com.xing.dev,并且在 file_path.xml中配置了如下内容:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path name="my_files" path="." />
</paths>

然后通过getExternalFilesDir获取文件保存目录,并且在该目录下有一个a.png文件:

java 复制代码
File file = new File(context.getExternalFilesDir(null), "a.png");
Uri contentUri = FileProvider.getUriForFile(context, "com.xing.dev.fileprovider", file);

那么,此时生成的 contentUri就应该是如下的样子:

xml 复制代码
content://com.xing.dev.fileprovider/my_files/a.png

当然,我们还可以配置更加复杂的路径:

如果我现在只想别的应用仅仅共享特定目录下的文件,那么我就可以配置更加具体的路径:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">  
    <external-files-path name="images" path="images/" />  
    <external-files-path name="docs" path="documents/" />
</paths>

这样,那么就只能访问 /external-files-path /images/ 和 /external-files-path/documents/ 文件夹下的文件了。

那么,除了external-files-path 这个外部私有存储目录中的文件路径,file_paths.xml中还支持哪些tag呢?

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Environment.getExternalStorageDirectory() -->
    <external-path name="external_files" path="." />

    <!-- Context.getExternalFilesDir(String) -->
    <external-files-path name="external_files_path" path="." />

    <!-- Context.getExternalCacheDir() -->
    <external-cache-path name="external_cache_path" path="." />

    <!-- Context.getFilesDir() -->
    <files-path name="internal_files_path" path="." />

    <!-- Context.getCacheDir() -->
    <cache-path name="internal_cache_path" path="." />
  
</paths>

目前来说,现在支持的是5个,分别是external-path,external-files-path,external-cache-path , files-pathcache-path, 可以通过下表来查看他们之间的含义:

假设我们的包名还是上面那个:com.xing.dev:

标签名称 对应java代码 路径Demo 含义
external-path Environment.getExternalStorageDirectory() /storage/emulated/0 整个外部存储目录
external-files-path Context.getExternalFilesDir(String) /storage/emulated/0/Android/data/com.xing.dev/files 应用的外部私有存储目录中的文件路径
external-cache-path Context.getExternalCacheDir() /storage/emulated/0/Android/data/com.xing.dev/cache 应用的外部私有存储目录中的缓存路径
files-path Context.getFilesDir() /data/user/0/com.xing.dev/files 应用的内部私有存储目录中的文件路径
cache-path Context.getCacheDir() /data/user/0/com.xing.dev/cache 应用的内部私有存储目录中的缓存路径

那么,如果我们理解了以上文件目录所代表的含义,那么使用FileProvider就非常容易了。总结下来,我们就可以有以下步骤:

  1. 确定我们需要共享文件的位置,是在整个外部存储目录中(这个显然不是特别安全,不推荐使用),还是在外部/内部私有文件/缓存目录中;
  2. 确定好之后,那么我们可以确定使用哪个标签;
  3. 确定好标签之后,如果还有更细粒度的要求,我们还可以加上子文件夹;
  4. 确定好 file_paths.xml之后,可以通过代码获取对应目标的文件夹,然后生成对应的 ContentURI;
  5. 最后就可以进行分享了。

对于一个越来越安全的android系统,使用FileProvider将App的存储目录分为5个部分,然后通过各自定义文件配置,实现对文件精细粒度的控制。

相关推荐
树℡独4 小时前
ns-3仿真之应用层(五)
服务器·网络·tcp/ip·ns3
嵩山小老虎5 小时前
Windows 10/11 安装 WSL2 并配置 VSCode 开发环境(C 语言 / Linux API 适用)
linux·windows·vscode
Fleshy数模5 小时前
CentOS7 安装配置 MySQL5.7 完整教程(本地虚拟机学习版)
linux·mysql·centos
a41324475 小时前
ubuntu 25 安装vllm
linux·服务器·ubuntu·vllm
Configure-Handler5 小时前
buildroot System configuration
java·服务器·数据库
津津有味道6 小时前
易语言TCP服务端接收刷卡数据并向客户端读卡器发送指令
服务器·网络协议·tcp·易语言
Fᴏʀ ʏ꯭ᴏ꯭ᴜ꯭.6 小时前
Keepalived VIP迁移邮件告警配置指南
运维·服务器·笔记
Genie cloud7 小时前
1Panel SSL证书申请完整教程
服务器·网络协议·云计算·ssl
一只自律的鸡7 小时前
【Linux驱动】bug处理 ens33找不到IP
linux·运维·bug
17(无规则自律)7 小时前
【CSAPP 读书笔记】第二章:信息的表示和处理
linux·嵌入式硬件·考研·高考