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

news2024/10/6 20:34:08

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);
	}
	
	@Override
	public void onActivityResult(int requestCode, int resultCode,Intent resultData) {
	     //使用resultdata.getdata ( )提取该URI
	    if (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);
        //显示结果toast
        showToast(" 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)进行介绍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1033938.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【神印王座】龙皓晨竟然上了头版头条!内容违背,新闻真实性原则

Hello,小伙伴们,我是小郑继续为大家深度解析神印王座国漫。 大家有没有发现,当龙皓晨他们从驱魔关回到圣城时,有这么一幕,一个卖报小孩边走边说:驱魔关大捷,少年英雄龙皓晨操控守护与怜悯之神印王座&#x…

使用Pugixml库,轻松处理XML文件

文章作者:里海 来源网站:王牌飞行员_里海_里海NX二次开发3000例,里海BlockUI专栏,C\C-CSDN博客 目录 1.介绍 2.Pugixml库 3.配置Visual Studio开发环境 4.节点 5.常见的节点类型 6.命名空间pugi 7.xml_document装载整个XML文档结构 8.xml_node文…

WeCanStudio工具套件介绍

直接上视频,在超燃的《天马座幻想》的背景音乐下,再次了解一下该工具套件吧。 WeCanStudio开发套件介绍

setrlimit限制进程的cpu使用时间

linux可以用setrlimit进行资源限制&#xff0c;今天说的是setrlimit限制进程的cpu使用时间 代码如下&#xff1a; #include <sys/time.h> #include <sys/resource.h> #include <unistd.h>int main() {struct rlimit rlim;rlim.rlim_cur 10;rlim.rlim_max …

星际争霸之小霸王之小蜜蜂(十四)--资本家的眼泪

系列文章目录 星际争霸之小霸王之小蜜蜂&#xff08;十三&#xff09;--接着奏乐接着舞 星际争霸之小霸王之小蜜蜂&#xff08;十二&#xff09;--猫有九条命 星际争霸之小霸王之小蜜蜂&#xff08;十一&#xff09;--杀杀杀 星际争霸之小霸王之小蜜蜂&#xff08;十&#xf…

stm32无人机-飞行力学原理

惯性导航&#xff0c;是一种无源导航&#xff0c;不需要向外部辐射或接收信号源&#xff0c;就能自主进行确定自己在什么地方的一种导航方法。 惯性导航主要由惯性器件计算实现&#xff0c;惯性器件包括陀螺仪和加速度计。一般来说&#xff0c;惯性器件与导航物体固连&#xf…

阿里云2核2G服务器e系列租用优惠价格182元性能测评

阿里云服务器经济型e实例2核2G配置优惠价格182.04元一年&#xff0c;系统盘ESSD Entry盘20GB起&#xff0c;公网带宽默认按使用流量&#xff0c;也可以选择按固定带宽计费&#xff0c;带宽值从1M到100M可选&#xff0c;阿腾云分享阿里云服务器2核2G优惠价格、详细配置及e系列CP…

异常记录-VS

1.文件加载失败 无法找到指定路径 Frame GUID: a6c744a8-0e4a-4fc6- 886a-064283054674 Frame mode: VSFM_ MdiChild Error code: 0x80131515 未理会这个提示&#xff0c;可以打开运行项目&#xff0c;只是会跳出这个弹窗。 无法关闭这个异常的窗口。

CorelDraw是什么软件?好用吗

很多人都听过CorelDraw的名字&#xff0c;但不知道CorelDraw是什么样的软件。下面就让小编为大家详细介绍一下。 coreldraw是什么软件 CorelDraw是一款专业的图形设计软件。它的主要功能包括矢量图形和位图的编辑。用户可以利用其矢量图形编辑能力,设计各种图标、Logo等精细图…

《算法竞赛·快冲300题》每日一题:“矩阵”

《算法竞赛快冲300题》将于2024年出版&#xff0c;是《算法竞赛》的辅助练习册。 所有题目放在自建的OJ New Online Judge。 用C/C、Java、Python三种语言给出代码&#xff0c;以中低档题为主&#xff0c;适合入门、进阶。 文章目录 题目描述题解C代码Java代码Python代码 “ 质…

每日已开源的AI论文分享【2023920期】

目录 前言 3D语义场景补全 视频修复 3D人脸重建 视频线条检测 3D物体重建 尾言 前言 作者介绍&#xff1a;作者本人是一名人工智能炼丹师&#xff0c;目前在实验室主要研究的方向为生成式模型&#xff0c;对其它方向也略有了解&#xff0c;希望能够在CSDN这个平台上与同…

【TypeScript】类、类的属性及抽象类(七)

【TypeScript】类、类的属性及抽象类&#xff08;七&#xff09; 【TypeScript】类、类的属性及抽象类&#xff08;七&#xff09;一、前言二、语法及属性定义三、类的继承四、属性类型五、readonly修饰符六、static 静态属性七、存取器-get/set八、抽象类 一、前言 传统的Jav…

定制SQLmap和WAF绕过

1. SQLmap tamper 脚本编写 以sqli-lab第26关为例 输入?id1’ --&#xff0c;报错字符型注入 考虑闭合问题&#xff0c;输入?id1’ and 1&#xff0c;但是回显中and和空格消失了&#xff0c;可知and和空格被过滤了 因为and和or被过滤考虑使用双写绕过手段&#xff0c;空格使…

2023华为杯数学建模D题第三问——区域双碳目标情景设计样例

在第二问建立好预测模型的基础上&#xff0c;如何设计第三问所说的区域双碳路径&#xff0c;以对宏观政策进行指导&#xff01; 采用STIRPA的基本模型对中国碳达峰时间进行预测&#xff0c;对该模型公式两边取对数得到&#xff1a; 其中&#xff1a;P为人口&#xff0c;A为GDP…

NIO圣经:一次穿透NIO、Selector、Epoll底层原理

此pdf电子书&#xff0c;是尼恩架构团队持续升级、持续迭代的作品。 目标是&#xff0c;通过不断升级、持续迭代&#xff0c;为大家构筑一个超底层、超强悍的高性能技术内功。 原 &#xff1a;《九阳真经&#xff1a;彻底明白操作系统 select、epoll 核心原理》 改&#xff1…

2023年研赛华为杯选题人数发布

选题人数发布&#xff01;经过长达30个小时&#xff0c;各个平台的相关选题投票、相关文章阅读量等各项数据进行统计&#xff0c;利用之前的评估办法&#xff08;详见注释&#xff09;。在开赛后30小时&#xff0c;我们基本确定各个赛题选题人数&#xff0c;大致为 题号选题人数…

时间轮算法

思考 假如现在有个任务需要3s后执行&#xff0c;你会如何实现&#xff1f; 线程实现&#xff1a;让线程休眠3s 如果存在大量任务时&#xff0c;每个任务都需要一个单独的线程&#xff0c;那这个方案的消耗是极其巨大的&#xff0c;那么如何实现高效的调度呢&#xff1f; 时…

兰贡生物:打造生物医药研发“独角兽”

当我们站在医学科学的最前沿&#xff0c;探索着无尽的健康奥秘时&#xff0c;生物制药行业正经历着前所未有的变革和机遇。新的疾病不断涌现&#xff0c;现有的治疗方法也在不断演进&#xff0c;这需要有着创新精神和科学追求的企业来推动新药研发的进程。值此时代背景下&#…

网络安全—黑客技术—自学笔记

目录梗概 一、自学网络安全学习的误区和陷阱 二、学习网络安全的一些前期准备 三、网络安全学习路线 四、学习资料的推荐 想自学网络安全&#xff08;黑客技术&#xff09;首先你得了解什么是网络安全&#xff01;什么是黑客&#xff01; 网络安全可以基于攻击和防御视角来…

Linux常用命令—find命令大全

文章目录 一、find命令常用功能1、find命令的基本信息如下。2、按照文件名搜索3、按照文件大小搜索4、按照修改时间搜索5、按照权限搜索举例&#xff1a;6、按照所有者和所属组搜索7、按照文件类型搜索8、逻辑运算符 一、find命令常用功能 1、find命令的基本信息如下。 命令名…