| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 移动开发 -> AndroidQ及以上的适配(一)-分区存储的适配 -> 正文阅读 |
|
[移动开发]AndroidQ及以上的适配(一)-分区存储的适配 |
目录 前言? ? ? ? 针对逐渐总结下AndroidQ及以上的适配。 一 Android存储????????Android的文件系统分为内部存储(Internal Storage)和外部存储(External Storage)。 1.内部存储(1)概念? ? ? ? 该存储区域主要存储的是数据库、SharedPreferences等系统数据。 ????????主要有三种类型的文件内容:
? ? ? ?这些内部存储的文件内容对普通的手机用户是无法查看的,但是开发者可以使用Android Studio自带的Device File Explore,如图: ???????? ?或者通过adb shell进行查看。其中data/data/应用包名下的文件夹下的内容如下: ???????? ?? ????????当执行设置的存储与缓存中的“删除数据”,会将该目录下的除去lidb的其他文件夹里面的内容给全部清空。 (2)Android提供的API
? ? ? ? ?获取的是/data/user/0/应用包名/cache,对应的上图中的data/data/应用包名/cache。当执行设置的存储与缓存中的“清空缓存”,会将这里面的内容给全部清空。
? ? ? ? ?获取的是/data/user/0/应用包名/files,对应的上图中的data/data/应用包名/files。
? ? ? ? 当然也可以通过该方法获取/data目录 ? ? ? ? 像getSharedPreferencesPath(String name)等其他API可以在使用的时候再去研究下。 2.外部存储(1)基本概念? ? ??????????主要存储的是任意想存储的文件内容 ???????? 在Android4.4之前,外部存储指的是SD卡。Android4.4之后,主要是手机内置的外部存储和可支持的SD卡。 ? ? ? ? 该区域的内容对于普通的手机用户就可以通过手机自带的文件管理器查看,如图: ???????? ? ? ? ? 该部分区域有分为两个区域:
????????? (2)Android提供的API? ? ? ? 在Context和Environment下都提供了API供开发者调用。
? ? ? ? 获取的是Android文件夹下的对应该包名的文件夹,type为传入的需要获取的文件夹的名字,返回的路径为/storage/emulated/0/Android/data/应用包名/files/文件夹名字。 ? ? ? ? 该区域下创建文件或文件夹,是不需要注册和动态申请READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限。?
? ? ? ? 获取的是Android文件夹下的对应该包名下的cache文件夹,返回的路径为/storage/emulated/0/Android/data/应用包名/cache。 当执行设置的存储与缓存中的“清空缓存”,会将这里面的内容给全部清空。
? ? ? ? 获取的是其他公共区域的文件夹,type为传入的需要获取的文件夹的名字,返回的路径为/storage/emulated/0/文件夹名字。 ? ? ? ? 注意从Android6.0开始,如果对外部存储进行读写文件,要向用户动态申请READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限。? ????????但是从Android10开始该方法已经废弃,开发者只能通过{@link Context#getExternalFilesDir(String)}, {@link MediaStore}?or {@link Intent#ACTION_OPEN_DOCUMENT}来访问这些公共区域的文件夹,在后面适配的时候,在详细看下这里的使用方式。 ? ? ? ? 上面的两个type对应的类型为: ???????? 二 Android10及以上的分区存储的适配? ? ? ? 从Android10开始,Google新增的分区存储(scoped storage)功能。该功能主要针对的是内置的外部存储区域。从上一节中可以看出外部存储区域主要包括两部分内容:一部分是APP私有的区域,另一部分就公有区域。 ????????在Android10之前,只要用户同意授权READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE之后,就可以在外部存储区域创建和读取文件,在应用卸载的时候,只要应用本身不做特殊处理,就不会删除这些文件,造成用户空间浪费;并且所有的应用都可以访问这些文件,所以回造成信息存在安全隐患,并造成用户存储空间有这些垃圾文件。 ? ? ? ? 从Android10之后,新增了分区存储的功能。对于APP私有区域,不需要用户授权,该APP就可以直接进行读写文件,其他APP也不可以访问,增加了本身应用的安全性;另外对于外部公有区域,只能访问在公共的多媒体文件夹如DCIM、Pictures等文件夹的文件;在读取文件类型的文档需要通过系统的文件选择器SAF。 1.分区存储? ? ? ? 在Android10中将外部存储空间的分区存储特性:
????????不需要读写权限就可以读写文件,可通过context.getExternalFilesDir(type)对“/storage/emulated/0/Android/data/应用包名”目录下的文件进行读写操作。但是该区域的文件会随着APP的卸载而清空。 ? ? ? ? 应用不能直接访问其他应用的私有目录。当执行设置的存储与缓存中的“删除数据”,会将该目录下的所有内容都删除。如果APP?想要在卸载时保留私有目录下的数据,要在AndroidManifest.xml中声明android:hasFragileUserData="true",这样在?APP卸载时就会有弹出框提示用户是否保留应用数据。 ????????访问该目录下的文件的相关API:
? ? ? ? 对于公共目录大体可分为两种类型: ????????一种就是存放多媒体文件,通常图片存储在DCIM和Pictures、视频文件存储在DCIM、Movies、Pictures、音频文件存储在Alarms、Music、Ringtones等;APP本身可以无需任何权限就可以在这些文件夹内增删改APP本身创建的对应类型的文件,但是如果读取其他APP创建的相关文件,需要用户授权READ_EXTERNAL_STORAGE,并且如果对非APP本身创建的多媒体文件进行修改或删除还需要用户额外授权同意对文件的修改。主要通过MediaStore下的一些API进行操作具体可见后面介绍。 ? ? ? ? 另外一种存放非多媒体文件,如PDF文件、txt文件等类型。下载的文件存储在Download;文档或者其他文件存储在Documents。同多媒体文件,如果APP本身创建的非多媒体文件可以进行增删改,但是对于非APP本身创建的,即使用户授权READ_EXTERNAL_STORAGE也无法进行访问,所以通过需要启动系统的文件选择器,让用户去选择可以访问哪些文件或者文件夹。当用户授权可以访问该文件之后,无需其他额外权限(即使无READ_EXTERNAL_STORAGE),就可以对该文件进行读取、编辑或删除。主要通过SAF来启动文件选择器。 ? ? ? ? 一般要按照文件的类型放置到对应的类型的文件夹下面。 ????????当然也可以在获取到读写权限之后,通过直接路径也可以访问,但是直接路径访问性能会不如使用MediaStore等方式。 ????????在Android10及以上,将外部存储区域进行分区存储之后,就对应着Legacy模式和Filtered模式两种模式:
? ? ? ? 对于上面两种模式的设置通过在Application标签上设置android:requestLegacyExternalStorage来区分。如果设置了应用的targetSdkVersion>=29的时候,会默认的为false,即设置为Filtered模式,开启了Android10的分区存储功能,在读取需要按照分区存储的特性进行读写文件;如果设置为true,则保持Android10之前的读写方式,但是Android11之后,停用了该功能,就是只能采用Filtered模式。 2.公共目录下多媒体文件操作的相关API? ? ? ? ? ?对多媒体文件主要通过MediaStore相关API来操作。关键点就是要找到对应的Uri?,配合ContentResolver就可以找到对应的相关文件的OutputStream和InputStream,然后通过Java IO就可以读写文件。 ? ? ? ? Uri又分为External、Internal以及可移动存储三种类型,每种文件类型对应的Uri的API可具体查看MediaStore下面,这里只列举几个: ????????如Image类型如下:
? ? ? ?像Audio、Video类型的只需要将Images缓存Audio、Video即可得到对应的三个Uri,而Downloads的Uri如下:
????????File类型的Uri只不过改成了MediaStore.Downloads.EXTERNAL_CONTENT_URI和MediaStore.Files.Media.getContentUri。 ? ? ? ?使用MediaStore在读写文件的时候,不管有没有获取READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限,都能读写本APP在公共目录下新建的文件。若获取到READ_EXTERNAL_STORAGE权限可以读取任意的Audio、Image、Video等公共目录下的多媒体文件,但是不能读取File和Download下的有其他应用创建的非多媒体文件。
?(1)使用MediaStore新建文件? ? ??????????新建文件一个是要知道要把文件存放目录的Uri,?一个是新建文件的OutputStream。?Uri就是执行通过ContentResolver执行insert操作返回的Uri,有了Uri就可以执行ContentResolver.openOutputStream()得到文件的OutputStream,而文件的属性内容通过ContentValues来赋值,代码如下:
? ? ? ? 这样有了?OutputStream就可以直接使用Java的IO操作进行写文件了。注意RELATIVE_PATH这个属性是可以在公共目录下设置子文件夹,并且只能在公共目录下的创建子文件夹,否则会抛出以下异常:
? ? ? ? 当然?ContentResolver不仅提供了获取到OutputStream的方法,还提供了下面获取一系列相关类型的文件的方式: ?????????? ?(2)使用MediaStore读取文件? ? ? ? 在读取文件的时候,也是两个关键的元素:一个是该文件的Uri,一个是该文件的InputSteam,有了InputStream就可以直接使用Java读写文件的方式进行读写文件了。 ? ? ? ? 那么多媒体文件可以直接通过ContentResolver.query来查出对应文件的Uri,如果是本APP创建的文件,可以直接通过ContentResolver.query得到文件对应的Uri,但是如果是非APP本身创建的文件,如多媒体文件,需要用户授权READ_EXTERNAL_STORAGE才可以获取到非APP本身创建的多媒体文件,而非多媒体文件是无法通过MediaStore来获取。 ? ? ? ? 首先先看如何获取该公共目录下的所有文件,代码如下:
? ? ? ? 那如果是要找某个特定的文件呢?这个还没有研究明白,因为在设置selection条件的时候,发现MediaStore.MediaColumns.TITLE和MediaStore.MediaColumns.DISPLAY_NAME还是有区别的(通过几个手机验证发现不管给MediaStore.MediaColumns.TITLE赋值的字符串有没有后缀名,会自动去掉后缀名后面的内容,而MediaStore.MediaColumns.DISPLAY_NAME是所有的文件名字),这个条件语句暂时不知道该怎么写。(遗留问题:怎么设置这个条件语句呢?暂时使用的是匹配的MediaStore.MediaColumns.DISPLAY_NAME与给定的fileName相等,则该Uri就是该文件对应的Uri) ? ? ? ? 暂时先使用前面的while循环中获取到的Uri作为要读取文件的Uri,有了Uri之后,就可以得到InputStream了,代码如下:
(3)使用MediaStore修改文件? ? ? ? 前面多次提到获取文件的方式:? ? ? ? ????????如果是修改本APP创建的文件,可直接通过ContentResolver.query来查出对应文件的Uri?,然后通过ContentResolver.openOutputStream(uri)得到文件的OutputStream,然后就可以直接通过Java IO进行操作文件就可以了。 ? ? ? ? 但是如果是其他APP创建的多媒体文件,首先要通过申请READ_EXTERNAL_STORAGE权限,经用户同意之后才可以访问其他应用创建的多媒体文件,然后通过ContentResolver.query来查出对应文件的Uri?,然后通过ContentResolver.openOutputStream(uri)得到文件的OutputStream,但是在进行对OutputStream进行写操作的时候,会抛出RecoverableSecurityException异常:
? ? ? ? 通常需要捕获该异常的时候,要进行弹框提示用户授权,代码如下:
? ? ? ? ?用户界面如下: ???????? ?????????最终通过?onActivityResult()返回给开发者,可以通过获取到的requestCode进行处理后续逻辑。 ?(4)使用MediaStore删除文件? ? ? ? 通过ContentResolver.query来查出对应文件的Uri?,可直接通过context.getContentResolver().delete(uri, null),同样对于其他应用创建的多媒体文件要申请READ_EXTERNAL_STORAGE权限,经用户同意之后才可以删除该文件,同样在删除的时候也会抛出RecoverableSecurityException异常,同? 3)使用MediaStore修改文件。 ? ? ? ? 但是该功能是Android11及以上才可以使用的API。 ?(5)使用MediaStore批量处理文件? ? ? ? 从Android11之后,可以批量处理文件。主要API如下:
? ? ? ? 向用户申请对指定的多媒体文件组的写入访问权限的请求
? ? ? ? 向用户申请对指定的多媒体文件组标记为收藏的请求。任何具有READ_EXTERNAL_STORAGE权限的应用都可以看到被用户标记为收藏的文件
? ? ? ? 向用户申请将指定的多媒体文件组放入到设备的垃圾箱的请求。有效期默认为7天之后删除
? ? ? ? 向用户申请将指定的多媒体文件组立即删除的请求。 ????????遗留问题:这个还没有搞懂怎么用 3.公共目录下非多媒体文件的操作的相关API? ? ? ? 从Android4.4就引入了存储访问框架(SAF)。包括三个元素:
? ? ? ? 所以不仅仅针对的是非多媒体文件,多媒体文件也可以通过该API获取。 ? ?(1)选择单个文件? ? ? ? 通过下面的方法可选择单个文件?
? ? ? ? 然后就会弹出界面选择对应需要操作的文件,仅仅是公共目录下的相关文件夹,如图: ???????? ?????????当用户选中对应的文件之后,就会通过onActivityResult()将该文件对应的Uri返回给开发者,那么有了该Uri就可以获取到OutputStream和InputStream,然后进行操作文件了。
? ? ? ? 只要用户选择了对应的文件(不管是不是该APP创建的文件还是其他APP创建的文件),不需要在申请其他权限(包括像在前面提出的RecoverableSecurityException异常),就可以对文件进行修改或删除。 ? ? ? ? ?但是在OPPO? A92s Android 11上进行修改文件(不管是否为本APP创建的文件)的时候,抛出下面的异常:
? ? ? ? 遗留问题:但是其他手机包括模拟器Android11都是不会抛出这个异常,这个不知道是OPPO是不是做了特殊处理,但是第二天在调试的时候,就发现这个问题没有了,不知道当时是怎么出现的这个问题(包括后来把READ_EXTERNAL_STORAGE权限打开关闭都不会复现这个现象了)。 (2)选择整个目录? ? ? ? 可以通过下面的方式来选择目录,代码如下:
? ? ? ? 然后在弹出的界面中选择对应的文件夹之后,会弹出授权界面如下: ????????? ? ? ? ? 注意从在Android 11上,无法通过SAF选择External Storage根目录、Downloads目录以及APP的私有目录(Android/data、Android/obb)的相关内容。 ? ? ? ? ?同样最后的操作结果通过?onActivityResult()将该文件对应的Uri返回给开发者,开发者就可以直接该Uri进行处理,主要此时的Uri为treeuri,为类似于:content://com.android.externalstorage.documents/tree/primary%3ADownload%2F123,主要返回的是该文件夹的Uri,如果需要对里面的文件进行处理,需要通过下面的代码获取到每个文件的Uri:
(3)新建文件? ? ? ? 同样可以直接通过Intent添加新建文件的一些基本信息,然后通过下面的代码:
? ? ? ? 调起系统的访问器,然后就会显示文件的名字以及存放的位置:? ???????? ? ? ? ? 同样最后的操作结果通过?onActivityResult()将该文件对应的Uri返回给开发者,开发者就可以直接使用该Uri对文件进行增删改。 (4)删除文件? ? ? ? 获取了文件的Uri,就可以通过下面的代码来删除该文件:
4.访问APP本身的私有目录? ? ? ? 在1.内部存储已经提到了通过context.getExternalFilesDir(type)等方法就可以直接在APP本身的私有目录读写文件,并且不需要任何权限。 5.访问其他APP提供的私有目录? ? ? ? 通过实现自定义的DocmentsProvider来实现将本APP的私有目录提供给其他APP使用,因为这次主要总结的是AndroidQ及以上的适配问题,这个等后面单独去总结。 三 总结
? ? ? ? 终于勉强汇总完了,后面在细化下,继续加油。 |
|
移动开发 最新文章 |
Vue3装载axios和element-ui |
android adb cmd |
【xcode】Xcode常用快捷键与技巧 |
Android开发中的线程池使用 |
Java 和 Android 的 Base64 |
Android 测试文字编码格式 |
微信小程序支付 |
安卓权限记录 |
知乎之自动养号 |
【Android Jetpack】DataStore |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 | -2025/1/31 6:21:10- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |