聊聊对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 分钟前
如何利用指纹浏览器爬虫绕过Cloudflare的防护?
服务器·网络·python·网络爬虫
pk_xz1234562 小时前
Shell 脚本中变量和字符串的入门介绍
linux·运维·服务器
小珑也要变强2 小时前
Linux之sed命令详解
linux·运维·服务器
海绵波波1072 小时前
Webserver(4.3)TCP通信实现
服务器·网络·tcp/ip
九河云4 小时前
AWS账号注册费用详解:新用户是否需要付费?
服务器·云计算·aws
Lary_Rock4 小时前
RK3576 LINUX RKNN SDK 测试
linux·运维·服务器
幺零九零零5 小时前
【计算机网络】TCP协议面试常考(一)
服务器·tcp/ip·计算机网络
云飞云共享云桌面6 小时前
8位机械工程师如何共享一台图形工作站算力?
linux·服务器·网络
Peter_chq6 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
一坨阿亮7 小时前
Linux 使用中的问题
linux·运维