当前位置: 首页 > news >正文

【Android Framework系列】第16章 存储访问框架 (SAF)

1 概述

Android 4.4(API 级别 19)引入了存储访问框架 (Storage Access Framework)SAF让用户能够在其所有首选文档存储提供程序中方便地浏览并打开文档图像以及其他文件。 用户可以通过易用的标准 UI,以统一方式在所有应用和提供程序中浏览文件和访问最近使用的文件。
在这里插入图片描述
存储访问框架SAF包括以下内容:

  1. 文档提供程序 :ConentProvider的子类,允许存储服务显示其管理的文件。 文档提供程序作为 DocumentsProvider 类的子类实现。文档提供程序的架构基于传统文件层次结构。Android 平台包括若干内置文档提供程序,操作sd卡对应的为ExternalStorageProvider
  2. 客户端应用 :就是我们平时的app,它调用 ACTION_OPEN_DOCUMENT,ACTION_CREATE_DOCUMENT ,ACTION_OPEN_DOCUMENT_TREE这三种IntentAction,来实现打开,创建文档,以及打开文档树。
  3. 选取器 : 一种系统 UI,我们称为DocumentUi,允许用户访问所有满足客户端应用搜索条件的文档提供程序内的文档。这个DocumentUI无桌面图标和入口,只能通过上面的Intent访问。
    在SAF框架中,我们的app应用和DocumentProvider之间并不产生直接的交互,而是通过DocumentUi进行。

2 SAF框架的使用

上文已经讲过,SAF框架的使用是通过DocumentUI的选择器来间接进行的,没法直接进行文件的操作。
使用方法如下:

2.1 打开文件

private static final int READ_REQUEST_CODE = 42;
...public void performFileSearch() {Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);//过滤器只显示可以打开的结果intent.addCategory(Intent.CATEGORY_OPENABLE);//要搜索通过已安装的存储提供商提供的所有文档//intent.setType("*/*");startActivityForResult(intent, READ_REQUEST_CODE);}@Overridepublic void onActivityResult(int requestCode, int resultCode,Intent resultData) {//使用resultdata.getdata ( )提取该URIif (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {Uri uri = null;if (resultData != null) {uri = resultData.getData();Log.i(TAG, "Uri: " + uri.toString());showImage(uri);}}
}

返回Uri:

content://com.android.externalstorage.documents/document/primary%3ADCIM%2FCamera%2FIMG20190607162534.jpg

在这里插入图片描述

2.2 打开文件树

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, OPEN_TREE_CODE);private void handleTreeAction(Intent data){Uri treeUri = data.getData();//授予打开的文档树永久性的读写权限final int takeFlags = intent.getFlags()& (Intent.FLAG_GRANT_READ_URI_PERMISSION| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);getContentResolver().takePersistableUriPermission(uri, takeFlags);//使用DocumentFile构建一个根文档,之后的操作可以在该文档上进行mRoot = DocumentFile.fromTreeUri(this, treeUri);//显示结果toastshowToast(" open tree uri "+treeUri);
}

返回的Uri:

content://com.android.externalstorage.documents/tree/primary%3AColorOS

在这里插入图片描述

  1. 对于我们打开的文档树,系统会赋予我们对该文档树下所有文档的读写权限,因此我们可以自由的使用我们上面介绍的输入输出流或者文件的方式来进行读写,该授权会一直保留到用户重启设备。
  2. 但是有时候,我们需要能够永久性的访问这些文件的权限,而不是重启就需要重新授权,因此我们使用了takePersistableUriPermission方法来保留系统对我们的uri的授权,即使设备重启也不影响。
  3. 我们可能保存了应用最近访问的 URI,但它们可能不再有效 — 另一个应用可能已删除或修改了文档。 因此,应该调用 getContentResolver().takePersistableUriPermission() 以检查有无最新数据。
  4. 拿到了根目录的uri,我们就可用使用DocumentFile辅助类来方便的进行创建,删除文件等操作了。

2.3 创建文件 ACTION_CREATE_DOCUMENT

private void createDocument(){Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);//设置创建的文件是可打开的intent.addCategory(Intent.CATEGORY_OPENABLE);//设置创建的文件的minitype为文本类型intent.setType("text/*");//设置创建文件的名称,注意SAF中使用minitype而不是文件的后缀名来判断文件类型。intent.putExtra(Intent.EXTRA_TITLE, "123.txt");startActivityForResult(intent,CREATE_DOCUMENT_CODE);
}private void handleCreateDocumentAction(Intent data){if (data == null) {return;}BufferedWriter bw = null;try {OutputStream os = getContentResolver().openOutputStream(uri);bw = new BufferedWriter(new OutputStreamWriter(os));bw.write(" i am a text ");showToast(" create document succeed uri "+uri);} catch (IOException e) {e.printStackTrace();}finally {closeSafe(bw);}}

2.4 编辑文档

onActivityResult()中获取到Uri之后,就可以对这个uri进行操作:

    private void alterDocument(Uri uri) {try {ParcelFileDescriptor pfd = getContext().getContentResolver().openFileDescriptor(uri, "w");FileOutputStream fileOutputStream =new FileOutputStream(pfd.getFileDescriptor());fileOutputStream.write(("Overwritten by MyCloud at " + System.currentTimeMillis() + "\n").getBytes());// Let the document provider know you' re done by closing the stream.fileOutputStream.close()fileOutputStream.close();pfd.close();} catch (IOException e) {e.printStackTrace();}}

2.5 删除文档

如果您获得了文档的 URI,并且文档的 Document.COLUMN_FLAGSSUPPORTS_DELETE,便可以删除该文档。例如:

DocumentsContract.deleteDocument(getContentResolver(), uri);

2.6 DocumentFile类的使用

DocumentFilegoogle为了方便大家使用SAF进行文件操作,而推出的帮助类。它的apijavaFile类比较接近,更符合一般用户的习惯,且内部实质都是使用了DocumentsContact类的方法来对文件进行操作。也就是说,我们也可以完全不使用DocumentFile而是使用DocumentsContact来完成SAF框架提供的文件操作,DocumentFile提供了三个静态工厂方法来创建自身。

fromSingleUri,该方法需要传入一个SAF返回的指向单个文件的uri,我们的ACTION_OPEN_DOCUMENT,ACTION_CREATE_DOCUMENT返回的uri就是该类型,其对应的实现类为ingleDocumentFile,代表的是单个的文件。
fromTreeUri,该方法传入指向文件夹的uri,我们的ACTION_OPEN_TREE返回的就是该类型,其对应的实现类为TreeDocumentFile,代表的是一个文件夹。
fromFile,该方法传入普通的File类,是对file类的一个模拟。

DocumentFile的方法总结如下:
在这里插入图片描述

3 SAF框架原理

3.1 SAF框架的类关系图如下所示:

在这里插入图片描述

由类关系图可以看出,DocumentFile工具类最终是通过DocumentsContract来实现操作的,而DocumentsContract最终操作的ProviderDocumentsProviderDocumentsProvider有三类:

ExternalStorageProvider是外置SD卡对应的ProviderDownloadStorageProvider是下载对应的Provider

ExternalStorageProvider:com.android.externalstorage.documents
DownloadStorageProvider:com.android.providers.downloads.documents
MediaDocumentProvider:com.android.providers.media.documents

下面具体分析下创建,修改,删除文件的流程
可以看出DocumentFile辅助类最终也是通过DocumentsContract来操作DocumentsProvider
在这里插入图片描述

下面看下跳到选择PickerUI的流程:
PickerUI最终也调到了DocumentsContract中。
在这里插入图片描述

3.2 DocumentProvider中的文档组织形式

在文档提供程序内,数据结构采用传统的文件层次结构,如下图所示:
在这里插入图片描述

  1. 每个DocumentProvider都可能有1个或多个做为文档结构树的Root根目录,每个根目录都有唯一的COLUMN_ROOT_ID,并且指向该根目录下表示内容的文档。
  2. 每个根目录下都有一个文档,该文档指向1到n个文档,而其中的每个文档又可以指向1到N个文档,从而形成树形的文档结构。
  3. 每个Document都会有唯一的COLUMN_DOCUMENT_ID用以引用它们,文档id具有唯一性,并且一旦发放就不得更改,因为它们用于所有设备重启过程中的永久性 URI 授权。
  4. 文档可以是可打开的文件(具有特定 MIME 类型)或包含附加文档的目录(具有 MIME_TYPE_DIR MIME 类型)。
  5. 每个文档都可以具有不同的功能,如 COLUMN_FLAGS 所述。例如,FLAG_SUPPORTS_WRITE、FLAG_SUPPORTS_DELETE 和 FLAG_SUPPORTS_THUMBNAIL。多个目录中可以包含相同的 COLUMN_DOCUMENT_ID。
    Document:
    在这里插入图片描述

3.3 自定义DocumentProvider

如果你希望自己应用的数据也能在documentsui中打开,你就需要写一个自己的document provider。(如果只是普通的文件操作,则不需要这么定义)
1)首先需要在Manifest中声明自定义的provider
在这里插入图片描述

2)实现DocumentProvider的基本接口
在这里插入图片描述
在这里插入图片描述

4 SAF框架总结

1. SAF框架,并不是直接与与DocumentProvider直接打交道,而是通过DocumentUI来间接操作。
2. 无论是通过Intent的方式,还是通过辅助类DocumentFile来进行文件操作,都需要获取uri,这个uri只能通过DocumentUI来返回,所以不是很方便。如果能接受通过DocumentUI来交互的,用SAF框架基本可以替代原有的文件操作方法

本章节大概了解SAF框架,我们下一章将对Android Q的沙箱模式(Scoped Storage)进行介绍

相关文章:

【Android Framework系列】第16章 存储访问框架 (SAF)

1 概述 Android 4.4(API 级别 19)引入了存储访问框架 (Storage Access Framework)。SAF让用户能够在其所有首选文档存储提供程序中方便地浏览并打开文档、图像以及其他文件。 用户可以通过易用的标准 UI,以统一方式在所有应用和提供程序中浏…...

Antdesign 4中让分页组件居中显示的方法

在Ant Design 4中分页组件默认是最右边显示的,而这个没有设置位置的属性的 解决办法: 在pagination的属性中增加: style: {textAlign: "center"} 在Ant Design 5中可以让pagination使用align: center来实现分页组件居中...

【笔记】ubuntu 20.04 + mongodb 4.4.14定时增量备份脚本

环境 ubuntu 20.04mongodb 4.4.14还没实际使用(20230922)后续到10月底如果有问题会修改 原理 只会在有新增数据时生成新的备份日期目录备份恢复时,如果恢复的数据库未删除,则会覆盖数据 准备 准备一个文件夹,用于…...

c++实现的一个定时器实例

/* * author: hjjdebug * date : 2023年 09月 23日 星期六 11:52:29 CST * description: 用std::thread 实现了一个定时器,深刻理解一下定时器是怎样工作的. * 参考Timer.h, Timer.cpp */ $ cat main.cpp #include "Timer.h" #include <unis…...

Python线程和进程

1、深度解析Python线程和进程 一篇文章带你深度解析Python线程和进程 - 知乎使用Python中的线程模块&#xff0c;能够同时运行程序的不同部分&#xff0c;并简化设计。如果你已经入门Python&#xff0c;并且想用线程来提升程序运行速度的话&#xff0c;希望这篇教程会对你有所帮…...

算法 寻找峰值-(二分查找+反向双指针)

牛客网: BM19 题目: 寻找数组峰值&#xff0c;可能多个返回任一个&#xff0c;每个值满足nums[i] ! nums[i 1] 思路: 双指针 left 0, right n-1, 相向而行&#xff0c;取中间位置mid, nums[mid]与nums[mid1]比较&#xff0c;如果nums[mid] < nums[mid1]&#xff0c;说明…...

【数据结构】—交换排序之快速排序究极详解,手把手带你从简单的冒泡排序升级到排序的难点{快速排序}(含C语言实现)

食用指南&#xff1a;本文在有C基础的情况下食用更佳 &#x1f525;这就不得不推荐此专栏了&#xff1a;C语言 ♈️今日夜电波&#xff1a;靴の花火—ヨルシカ 0:28━━━━━━️&#x1f49f;──────── 5:03 …...

【c#-Nuget 包“在此源中不可用”】 Nuget package “Not available in this source“

标题c#-Nuget 包“在此源中不可用”…但 VS 仍然知道它吗&#xff1f; (c# - Nuget package “Not available in this source”… but VS still knows about it?) 背景&#xff1a; 今日从公司svn 上拉取很久很久以前的代码&#xff0c;拉取下来200报错&#xff0c;进一步发…...

【数据结构】二叉树之堆的实现

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;数据结构 &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、二叉树的顺序结构 &#x1f4d2;1.1顺序存储 &#x1f4d2;1.2堆的性质…...

电工-三极管输入输出特性曲线讲解

三极管特性曲线是反映三极管各电极电压和电流之间相互关系的曲线&#xff0c;是用来描述晶体三极管工作特性曲线&#xff0c;常用的特性曲线有输入特性曲线和输出特性曲线。这里以下图所示的共发射极电路来分析三极管的特性曲线。 输入特性曲线 该曲线表示当e极与c极之间的电…...

深入解析容器与虚拟化:技术、对比与生态

深入解析容器与虚拟化&#xff1a;技术、对比与生态 文章目录 深入解析容器与虚拟化&#xff1a;技术、对比与生态容器和虚拟化的基本概念和原理容器的定义和特点虚拟化的定义和特点 容器使用场景容器和虚拟机的对比虚拟化技术的四个特点容器实现虚拟化的原理常见容器引擎和容器…...

制作游戏demo的心得

制作这个游戏demo出来的心得 https://www.bilibili.com/video/BV1cF411m7Dh/ 制作游戏demo的心得 制作游戏demo&#xff0c;主要是为了表现自己的技术&#xff0c;那就一门心思想着如何提高表现力就行了&#xff0c;在整体的画面渲染风格方面或许没有什么可选择的&#xff0c;…...

Web Tour Server窗口闪现

1.打开该文件所在位置 2.右击选择编辑&#xff0c;在最后一行加上pause&#xff0c;保存后重新打开Server窗口 3.重新打开后&#xff0c;若出现以下情况&#xff1a; 以管理员身份打开cmd命令行&#xff0c;输入命令netstat -aon|findstr “1080”&#xff0c;查看1080端口占用…...

Linux下的基本指令

目录 01. ls 指令 02. pwd命令 03. cd 指令 04. touch指令 05.mkdir指令&#xff08;重要&#xff09;&#xff1a; 06.rmdir指令 && rm 指令&#xff08;重要&#xff09;&#xff1a; 07.man指令&#xff08;重要&#xff09;&#xff1a; 08mv指令&#xff…...

随机数生成器代码HTML5

代码如下 <!DOCTYPE html> <html> <head> <title>随机数生成器</title> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <style> body { text-align: center; bac…...

正确理解redux Toolkits中createSlice的action.payload

使用redux Toolkits中的createSlice编写extraReducers经常看到使用action.payload来更新state状态值&#xff1a; 那么action.payload指的到底是什么&#xff1f; 让我们看看action的定义部分&#xff1a; 注意&#xff1a; action.payload不是上面ajax请求的返回内容&#x…...

YOLOv8快速复现 官网版本 ultralytics

YOLOV8环境安装教程.&#xff1a;https://www.bilibili.com/video/BV1dG4y1c7dH/ YOLOV8保姆级教学视频:https://www.bilibili.com/video/BV1qd4y1L7aX/ b站视频&#xff1a;https://www.bilibili.com/video/BV12p4y1c7UY/ 1 平台搭建YOLOv8 平台&#xff1a;https://www.a…...

Haproxy搭建 Web 群集实现负载均衡

目录 1 Haproxy 1.1 HAProxy的主要特性 1.2 HAProxy负载均衡策略 1.3 LVS、Nginx、HAproxy的区别 2 Haproxy搭建 Web 群集 2.1 haproxy 服务器部署 2.1.1 关闭防火墙 2.1.2 内核配置&#xff08;实验环境可有可无&#xff09; ​2.1.3 安装 Haproxy 2.1.4 Haproxy服务…...

Tessy 5.0.4

Tessy 5.0.4 Linux 2692407267qq.com&#xff0c;更多内容请见http://user.qzone.qq.com/2692407267/...

mybatis-plus根据指定条件批量更新

1.service实现类中 比如我这里只针对UserEntity&#xff0c;在UserServiceImpl下&#xff08;该实现类是继承了mybatis-plus的ServiceImpl的&#xff09;新增如下代码&#xff1a; public boolean updateBatchByQueryWrapper(Collection<UserEntity> entityList, Funct…...

虹科方案 | LIN/CAN总线汽车零部件测试方案

文章目录 摘要一、汽车零部件测试的重要性&#xff1f;二、虹科的测试仿真工具如何在汽车零部件测试展露头角&#xff1f;三、应用场景**应用场景1&#xff1a;方向盘开关的功能测试****应用场景2&#xff1a;各类型电机的控制测试****应用场景3&#xff1a;RGB氛围灯的功能测试…...

[solidity]合约调用合约

先写一个简单的合约将其部署&#xff0c;部署后的合约地址为&#xff1a;0xd9145CCE52D386f254917e481eB44e9943F39138 // SPDX-License-Identifier: MIT pragma solidity ^0.8.0;contract A{string myname;function setName(string memory _name) public{myname_name;}functi…...

Vulnhub系列靶机---JANGOW 1.0.1

文章目录 网卡配置信息收集主机发现端口扫描 漏洞利用反弹Shell提权 靶机文档&#xff1a;JANGOW 1.0.1 下载地址&#xff1a;Download (Mirror) 难易程度&#xff1a;. 网卡配置 水果味儿 信息收集 主机发现 端口扫描 访问80端口 点击site目录 点击页面上方的一个选项&…...

肖sir__项目环境之全流程__005

一、测试流程&#xff08;h模型&#xff09; 1、需求文档&#xff08;产品&#xff09; 需求文档&#xff08;软件需求规格说明书srs&#xff09; &#xff08;1&#xff09;如何分析需求 a、显示需求&#xff08;主流程、功能&#xff0c;业务&#xff09; b、隐性需求&#x…...

搜狗输入法下键翻页

搜狗输入法下键翻页 从官网下载 搜狗输入法智慧版关闭超级候选关闭候选...

C#多线程

一、多线程实现方式 1. 使⽤Thread类&#xff1a; System.Threading.Thread 类是C#中最基本的多线程编程⼯具。 2. 使⽤ThreadPool&#xff1a; 线程池是⼀个管理和重⽤线程的机制&#xff0c;它可以在应⽤程序中创建和使 ⽤多个线程&#xff0c;⽽⽆需显式地管理线程的…...

Unity 编辑器常用方法

unity编辑器开发 脚本注解1. RuntimeInitializeOnLoadMethod2. ColorUsage3. Header4. SerializeField5. HideInInspector6. Space7. Range8. Multiline9.[RequireComponent(typeof())]10.HelpURL 右键菜单注解1. CreateAssetMenu - 针对ScriptableObject 菜单栏注解1. MenuIt…...

21 mysql ref 查询

前言 这里主要是 探究一下 explain $sql 中各个 type 诸如 const, ref, range, index, all 的查询的影响, 以及一个初步的效率的判断 这里会调试源码来看一下 各个类型的查询 需要 lookUp 的记录 以及 相关的差异 此系列文章建议从 mysql const 查询 开始看 测试表结构…...

启山智软/一款包含主流商城类型的一款电商中台系统100%开源

文章目录 介绍一、Smart Shop JAVA 微服务电商中台优势二、电商中台包含那些主流商城模式1.S2B2C供应链商城2.B2B2C多商户商城3.B2C单商户商城4.O2O外卖配送商城5.社区团购商城 6.演示地址总结 介绍 想要了解代码规范&#xff0c;学习商城解决方案&#xff0c;点击下方官网链接…...

【C语言】指针的进阶(四)—— 企业笔试题解析

笔试题1&#xff1a; int main() {int a[5] { 1, 2, 3, 4, 5 };int* ptr (int*)(&a 1);printf("%d,%d", *(a 1), *(ptr - 1));return 0; } 【答案】在x86环境下运行 【解析】 &a是取出整个数组的地址&#xff0c;&a就表示整个数组&#xff0c;因此…...

做网站从哪方面入门/成都seo排名

开门见山。 PMP是应聘项目管理、项目经理这些岗位的敲门砖&#xff0c;真正引进国内是在1999年&#xff0c;那时候大家对这个证书了解不多&#xff0c;一年也就百八十人报考&#xff0c;随着PMP越来越被市场认可&#xff0c;到目前国内认证人数已经近百万&#xff0c;做过工程…...

重庆市建设工程信息网招标文件/网站seo基础优化

欢迎和号主【前端点线面】进群盘算法&#xff0c;此外本号干货满满&#xff1a;14个门类&#xff08;100篇原创&#xff09;内容&#xff08;又干又硬&#xff09;、《前端百题斩》pdf&#xff08;助力薪资double&#xff09;、20篇思维导图&#xff08;知识系统化、记忆简单化…...

做美食网站需求分析报告/网站建设网络营销

1、 boot分区 大小&#xff1a;一般300Mb左右 作用&#xff1a;引导分区&#xff0c;包含了系统启动的必要内核文件&#xff0c;即使根分区损坏也能正常引导启动&#xff0c;一般这些文件所占空间在200M以内。 分区建议&#xff1a;分区的时候可选100M-500M之间,如果空间足够…...

网站平台怎么做的好处/营销策划公司靠谱吗

BAD_ACCESS 在什么情况下出现 BAD_ACCESS 报错属于内存访问错误&#xff0c;会导致程序崩溃&#xff0c;错误的原因是访问了野指针(悬挂指针)。野指针指的是本来指针指向的对象已经释放了&#xff0c;但指向该对象的指针没有置 nil&#xff0c;指针指向随机的未知的内存&#…...

网站里面内外链接如何做/2022知名品牌营销案例100例

实验十四 课程学习总结 项目内容这个作业属于哪个课程(https://www.cnblogs.com/nwnu-daizh/)这个作业的要求在哪里(https://www.cnblogs.com/nwnu-daizh/p/11093584.html)课程学习目标掌握软件项目评审会流程&#xff1b;反思总结课程学习内容。1.结合本学期课程学习内容&…...

做网站制作较好的公司/成都疫情最新消息

一&#xff0e;简介 0. 页面的生命周期。 1. WebForm后台页面类继承于Page类&#xff0c;Page类实现了IHttpHandler接口。 2. 前台页面类继承于后台页面类。 3. 先调用PageLoad方法&#xff0c;再调用Render方法生成html代码。 二. 加密安全 互联网没有绝对的安全&#xff0c;登…...