Android 存储进化:分区存储


/storage/emulated/0/Android/data/包名

Android 10的分区存储特性,对Android系统的外部存储空间重新设计,外部存储被分为应用私有目录以及共享目录两个部分:

  1. 应用私有目录:存储应用私有数据,外部存储应用私有目录对应Android/data/包名

  2. 共享目录:存储其他应用可访问文件, 包含媒体文件、文档文件以及其他文件,对应设备DCIM、Pictures、Alarms, Music, Notifications,Podcasts, Ringtones、Movies、Download等目录。

2.2.1 应用私有空间

与以往相同的是,访问自身的外部存储下的应用私有空间是不需要任何权限的。与内部一样,也有一个目录专为应用的持久性文件而设计,和另一个目录包含应用的缓存文件。也是可以正常使用File相关api的,所以只要取得路径便可自由发挥。

需要注意的不同点是:开启了分区存储特性后,应用只能访问自身的私有空间,即使获得了存储权限,也无法访问其他应用的私有空间

另外与内部空间的区别是,外部存储空间可能被移除也可能有多个,所以返回的是一个数组,对于返回数组中的第一个元素被视为主外部存储卷。除非该卷已满或不可用,否则请使用该卷。

  1. 持久性文件:getExternalFilesDirs(@NonNull Context context, @Nullable String type),type根据文件类型可传系统预定义的子目录常量,如图片Environment.DIRECTORY_PICTURES,此时返回/storage/emulated/0/Android/data/包名/files/Pictures。或者传null直接返回/storage/emulated/0/Android/data/包名/files

  2. 缓存性文件:ContextCompat.getExternalCacheDirs(context)/storage/emulated/0/Android/data/包名/cache

注意:卸载app后,系统会自动移除这些目录释放空间!!

三 共享存储空间的影响

=========================================================================

如果用户数据可供或应可供其他应用访问,并且即使在用户卸载应用后也可对其进行保存,请使用共享存储空间。

共享文件类型, 包含媒体文件、文档文件以及其他文件,对应设备DCIM、Pictures、Alarms, Music, Notifications,Podcasts, Ringtones、Movies、Download等目录。Android 分别提供用于获得该类型可共享数据文件Uri的 API:

  • 媒体内容:可以使用 MediaStore API 访问此内容

  • 文档和其他文件:系统有一个特殊目录,用于包含其他文件类型,例如 PDF 文档和采用 EPUB 格式的图书。应用可以使用Storage Access Framework访问这些文件。

对于共享文件,。以往可以通过data column获得路径,再使用File API来操作,现在都会返回失败。开启了分区存储特性之后,应用只能通过系统提供的api来向系统请求得到对应文件的Uri,并通过Uri生成FileDescriptorInputStream等方式进行文件读写:(简而言之,对于共享文件的增删查改,主要问题在于Uri的获取)

注:android 11 又允许通过路径来访问,系统会自动重定向为Uri。

val resolver = applicationContext.contentResolver

//读

resolver.openFileDescriptor(content-uri, “r”)?.use { pfd ->

val inputStream = FileInputStream(pfd.fileDescriptor)

}

resolver.openInputStream(content-uri).use { stream ->

}

//写

resolver.openFileDescriptor(content-uri, “w”)?.use { pfd ->

val outputStream = FileOutputStream(pfd.fileDescriptor)

}

resolver.openOutputStream(content-uri).use { stream ->

}

//图片bitmap

BitmapFactory.decodeFileDescriptor(pfd.fileDescriptor)

3.1 MediaStore API


MediaStrore API的增删查改,可参看Google官方指南,主要是通过contentResolver获得对应的uri,这里就不引入了。图片来源

3.1.1 MediaStore 概述

Android系统会自动扫描外部存储空间,将媒体文件按类型添加到系统预定义的Images、Videos、Audio files、Downloaded files集合中。Android Q通过MediaStore.Images、MediaStore.Video、MediaStore.Audio、MediaStore.Downloads 访问相对应共享目录文件资源。预定义集合所对应的目录如下表所示:

| 媒体类型 | Uri | 默认创建目录 | 允许创建目录 |

| — | — | — | — |

| Image | content://media/external/images/media | Pictures | DCIM,Pictures |

| Audio | content://media/external/audio/media | Music | Alarms,Music,Notifications,Podcasts,Ringtones |

| Video | content://media/external/video/media | Movies | DCIM,Movies |

| Download | content://media/external/downloads | Download | Download |

注意:MediaStore.Downloads.EXTERNAL_CONTENT_URI是Android10版本新增API,用于创建、访问非媒体文件

3.1.1 MediaStore 的变化

  1. MediaStore API在共享目录指定目录下创建文件或者访问应用自己创建文件,不需要申请存储权限;

  2. MediaStore API访问其他应用在共享目录创建的媒体文件(图片、音频、视频), 需要申请存储权限,未申请存储权限,通过ContentResolver查询不到文件Uri,即使通过其他方式获取到文件Uri,读取或创建文件会抛出异常;

  3. MediaStore API不能够访问其他应用创建的非媒体文件(pdf、office、doc、txt等), Android 10 里唯一一种访问其他应用创建的非媒体文件的途径是使用存储访问框架 (Storage Access Framework) 提供的文档选择器。

3.1.2 通过api创建的文件存放到哪里?如何自定义位置?

当通过MediaStore API创建文件时,文件会默认保存到对应的类型目录,比如图片存到Pictures/目录下,可以往上查看表格的默认目录及允许目录;

可以使用MediaStore.xxx.Media.RELATIVE_PATH自己指定要存放的目录或者子目录,如:contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/自定义子目录"),文件就会放在Pictures/自定义子目录/ 中;或者使用contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment. DIRECTORY_DCIM),将文件放到DCIM/

注意:每一种类型都有对应的可允许创建的目录,否则会返回失败。具体可创建目录可以往上查看表格

3.2 Storage Access Framework


SAF框架支持用户与系统选择器互动,从而选择文档提供器以及供您的应用创建、打开或修改的特定文档和其他文件。由于用户参与了文件的选择,因此该机制无需任何系统权限。

应用通过调用 ACTION_CREATE_DOCUMENT , ACTION_OPEN_DOCUMENT , 和ACTION_OPEN_DOCUMENT_TREE Intent获取Document provider提供的文件,并在onActivityResult接口接收返回的被选择文件的Uri。另外,在配置 intent 时,应指定文件的名称和 MIME 类型,并且还可以根据需要使用 EXTRA_INITIAL_URI intent extra 指定文件选择器在首次加载时应显示的文件或目录的 URI。

这部分也是没变化的,可参考官方指南:从共享存储空间访问文档和其他文件

3.2.1 获取持久权限

对于通过SAF框架获得的uri权限,可以通过申请持久权限,不用每次重启手机都要重新请求。

contentResolver.takePersistableUriPermission(

documentUri,

Intent.FLAG_GRANT_READ_URI_PERMISSION

)

四 存储特性Android版本差异概览

=================================================================================

4.1 其他变化:图片位置信息


一些照片在元数据中包含位置信息,以便用户查看照片的拍摄地点。由于此位置信息属于敏感信息,如果应用使用了分区存储,默认情况下 Android 10 会对应用隐藏此信息。

如果应用需要访问照片的位置信息:

  1. 在应用清单中请求ACCESS_MEDIA_LOCATION权限

  2. 通过调用 setRequireOriginal(),从 MediaStore 对象获取照片的确切字节,并传入照片的 URI

五 更新

==================================================================

5.1 Android 10 的分区存储"bug"


Android 10 通过媒体MediaStore API 删除(delete)一个媒体文件,只是简单移除了MediaStore数据库的索引,并不会真正删除物理存储上的实体文件,而且只要手机重启,则索引又被加上去了。issue

这需求也比较少见,只是刚好测试发现了。网上查了下,的确存在这个问题,Android 11 就可以正常删除了。 要是有什么解决方案,热烈欢迎指出!!

5.2 Android 11 的存储变更


5.2.1 允许继续使用原始文件路径

可以再次使用文件路径,系统自动重定向为Uri

5.2.2 增加批量操作

在 Android 10 中,应用在对MediaStore的每一个文件请求编辑或删除时都必须一个个地得到用户的确认。而在 Android 11 中,应用可以一次请求修改或者删除多个媒体文件。

主要通过以下新增的批量操作api

| 方法 | 说明 |

| — | — |

| MediaStore.createDeleteRequest (resolver, uris) | 批量删除(不放入回收站) |

| MediaStore.createFavoriteRequest(resolver, uris) | 批量收藏 |

| MediaStore.createTrashRequest (resolver, uris) | 批量移入回收站 |

| MediaStore.createWriteRequest(resolver, uris) | 批量获得写入权限 |

val uris = …

val pi = MediaStore.createWriteRequest(contentResolver,

uris)

startIntentSenderForResult(pi.intentSender, REQUEST_CODE, null, 0, 0, 0)

//相应

override fun onActivityResult(xxx) {

when (requestCode) {

REQUEST_CODE ->

if (resultCode == Activity.RESULT_OK) {

//获得权限,继续操作

} else {

// 用户拒绝了权限授予

}

}

}

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

-1714945234164)]

[外链图片转存中…(img-JwlnrPB1-1714945234164)]

[外链图片转存中…(img-JWCeltJG-1714945234164)]

[外链图片转存中…(img-aHr1SFlI-1714945234164)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

  • 25
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值