IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android FileProvider 详解 -> 正文阅读

[移动开发]Android FileProvider 详解

一、前言

????FileProviderContentProvider 的一个特殊子类,它可以为应用生成关联的 content:// 内容 URI ,而不是 file:/// 类型的 URI,使得应用能够实现安全地共享文件。

????内容 URI 允许授予对文件临时的读/写访问权限。当您构建一个包含内容 URI 的 Intent,并要将包含内容 URI 的 Intent 传递给客户端应用,可以通过 Intent.setFlags() API 添加访问权限,这些权限在客户端应用的接收 Activity 处于激活状态时有效(接收 Activity 栈销毁时授权自动失效);如果 Intent 是传递给 Service,在 Service 运行期间权限有效(Service 停止销毁后授权自动失效)。与之相比,控制 file:/// 类型 URI 的访问权限是通过变更文件所在的文件系统的权限来实现的,授予的访问权限是针对所有应用可用的,并且除非您手动修改权限,否则一直有效,因此这类授权是非常不安全的。内容 URI 提供的更高级别的文件访问安全性,让 FileProvider 成为 Android 安全架构基础的关键部分。

二、定义 FileProvider

????由于 FileProvider 的默认功能包含为文件生成内容 URI,因此你不需要在代码中定义 FileProvider 的子类。只需要在 AndroidManifest.xml 清单文件中声明 FileProvider,在应用清单文件的 <application> 标签内部添加 <provider> 标签来声明 FileProvider 组件。设置 android:name 属性值为 androidx.core.content.FileProvider(AndriodX);设置 android:authorities 属性为 FileProvider 生成内容 URI 的授权,授权字符串必须保证唯一(通常使用包名组装);设置 android:exported 属性为 falseFileProvider 不需要对外公开);设置 android:grantUriPermissions 属性值为 true,允许给文件授予临时访问权限。如下示例代码所示:

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.owen.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <!-- ...... -->
</provider>

注意事项:
1. 对于您自己的应用,考虑使用 应用包名.fileprovider 的方式指定授权(亦可增加其他字符),防止不同应用间出现授权冲突;
2. 如果您需要重写 FileProvider 类修改默认实现,在 <provide> 标签的 android:name 属性值必须为类名全称。

三、指定可用文件

????FileProvider 只能为事先指定目录下的文件生成内容 URI。指定目录,也就是在 XML 资源文件中定义存储空间和路径。

3.1 创建 XML 资源配置

????首先需要创建一个 XML 资源文件,存放在 res/xml 目录下,XML 文件以 <patchs> 为根节点,在根节点下必须一个或者多个表示存储空间和路径的节点,如下示例所示:

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

????在此 XML 文件中,<path> 可包含以下类型的子节点:

  • <files-path>:表示在应用内部存储空间中 files/ 子目录,这个目录路径跟 Context.getFilesDir() 返回的一致。
  • <cache-path>:表示在应用内部存储空间中 cache/ 子目录,这个目录路径跟 Context.getCacheDir() 返回的一致。
  • <external-path>:表示在应用外部存储空间中的根目录,这个目录路径跟 Environment.getExternalStorageDirectory() 返回的一致。
  • <external-files-path>:表示在应用外部存储空间中 files/ 子目录,这个目录路径跟 Context.getExternalFilesDir(String)Context.getExternalFilesDir(null) 返回的一致。
  • <external-cache-path>:表示在应用外部存储空间中 cache/ 子目录,这个目录路径跟 Context.getExternalCacheDir() 返回的一致。
  • <external-media-path>:表示在应用外部存储空间中媒体子目录,这个目录路径跟 Context.getExternalMediaDirs() 返回的一致(注意:这个目录只在 API 21+ 的设备上有效)。

????在这些表示目录路径的子节点中,都包含以下两个属性:

  • name:内容 URI 路径片段。为了增强安全性,这个值用来隐藏文件子目录的详细路径信息,也就是在内容 URI 中,用这个属性值替代子目录的路径信息。
  • path:需要共享文件所在的子目录详细路径,这个值是真实存在的路径。必须注意的是,这个属性值必须是一个子目录,而不能特定的文件或者一系列文件。你可以通过文件名共享单个文件,但是不能使用通配符指定多个文件。

示例res/xml/file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="picture" path="internal/pic/"/>
    <files-path name="database" path="internal/db/"/>

    <external-path name="." path="."/>

    <external-files-path name="picture" path="picture/"/>
</paths>

讲解:以上的示例中,<files-path name="database" path="internal/db/"/> 这项声明表示可以共享应用内部存储下 files/internal/db 目录极其子目录下的文件。假如一个文件存储在 files/internal/db 目录下,在生成的内容 URI 中并不会包含 internal/db 片段,而是使用 name 属性的 值 database 隐藏了真实的路径信息。例如为 files/internal/db/init_data.db 生成的内容URI 为 content://com.owen.demo.android.owen.fileprovider/database/init_data.db

3.2 在 FileProvider 中引用目录配置

????在应用清单文件中的 <provider> 标签内部,使用 <meta-data> 子标签引用目录配置 XML 资源,其中 android:name 属性值必须是 android.support.FILE_PROVIDER_PATHSandroid:resources 引用定义好的 XML 资源文件,如下示例所示:

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

四、为文件生成内容 URI

????使用内容 URI 跟其他应用共享文件,您的应用必须生成内容 URI。配置好 ContentProvider 之后,就可以使用 FileProvider 生成文件的内容 URI。先为文件定义一个 File 实例,然后调用 FileProvider.getUriForFile() API ,传入<provider> 标签 android:authorities 属性声明的授权以及文件 File 实例,即可生成内容 URI。如下代码所示:

val dbFile = File(appContext.filesDir, "db/init_data.db")
val uri = FileProvider.getUriForFile(appContext, appContext.packageName + ".owen.fileprovider", dbFile);
println(uri.toString())

????通过调用 FileProvider.getUriForFile() API 生成的内容 URI 中包含 <provider> 标签 android:authorities 属性声明的授权,文件目录(XML 中 <meta-data> 指定的目录)相对应的相对路径,是 content://<authorities>/<path> 的形式。以上示例打印出来的内容 URI 如下所示:

content://com.owen.demo.android.owen.fileprovider/database/init_data.db

注意事项:
1. FileProvider.getUriForFile() 只能对 XML 文件声明的目录及其子目录下的文件生成内容 URI;
2. FileProvider.getUriForFile() 的授权(第二个参数)必须和清单文件中 provider 定义的授权一致。

4.1 授予内容 URI 临时访问权限

????生成的文件内容 URI 之后,你可以通过两种方式对内容 URI 授予访问权限,给特定的包名授予访问权限,或者在传递内容 URI 的 Intent 中包含访问权限。

4.1.1 给特定包名授予访问权限

????通过调用 Context.grantUriPermission(package, Uri, mode_flags) API 为 content:// 类型的 URI 进行授权,通过第三个参数(mode_flags)传入 Intent.FLAG_GRANT_READ_URI_PERMISSIONIntent.FLAG_GRANT_WRITE_URI_PERMISSION 这两个标志之一或者两个都传入。授予的访问权限会一直有效,直到调用 Context.revokeUriPermission(targetPackage, uri, modeFlags) API 取消授权,或者直到设备重启。

4.1.2 在 Intent 中授予访问权限

????将内容 URI 传递给请求方应用,并且赋予对内容 URI 的访问权限,按照以下步骤配置:

  1. 构建一个 Intent 实例对象,通过 Intent.setData() 将内容 URI 添加到 Intent 中;
  2. 调用 Intent.setFlags() 或者 Intent.addFlags() 接口添加 Intent.FLAG_GRANT_READ_URI_PERMISSIONIntent.FLAG_GRANT_WRITE_URI_PERMISSION 标志(或者同时添加两个);
  3. 将结果发送给其他应用,通常调用 ActivitysetResult() 方法。

????通过 Intent 授予的内容 URI 访问权限,在接收的 Activity 栈处于激活状态时保持有效,当栈销毁之后,授权将自动失效。授予客户端应用一个 Activity 的访问权限,也将会自动扩展到应用的其他组件。

为了支持在运行 Android 4.1 (API level 16) 和 Android 5.1 (API level 22) (含)的设备,以内容 URI 创建一个 ClipData 对象,并且为 ClipData 对象配置访问权限。

shareContentIntent.setClipData(ClipData.newRawUri("", contentUri));
shareContentIntent.addFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-31 16:45:30  更:2021-07-31 16:45:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/7 1:20:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码