什么是分区存储:
为了让用户更好地控制自己的文件并减少混乱,Android 10 针对应用推出了一种新的存储范例,称为分区存储。分区存储改变了应用在设备的外部存储设备中存储和访问文件的方式。以 Android 10(API 级别 29)及更高版本为目标平台的应用在默认情况下被授予了对外部存储空间的分区访问权限(即分区存储)。此类应用只能访问外部存储空间上的应用专属目录,以及本应用所创建的特定类型的媒体文件。
适配背景:由于app需要到google play上架,由于第一次提交该应用,google play认为是新应用,要求target sdk版本必须>=30(google play上的老应用没有这个限制)。于是把target sdk由28升到了30,又没有及时对分区存储进行适配,引发了一些问题。
目前谷歌对分区存储给了一个过渡期,由于国内应用市场并未对targetsdk版本做限制,网上适配也多以降级targetsdk的方式来解决。
Android 存储分区情况
Android 中存储可以分为两大类:专属存储和共享存储
专属存储 (Private Storage) : 每个应用在都拥有自己的专属目录,其它应用看不到,彼此也无法访问到该目录:
- 内部专属目录 (/data/data/packageName/) ;
- 外部专属目录 (/sdcard/Android/data/packageName/),
共享存储 (Shared Storage) : 存储其他应用可访问文件, 包含媒体文件、文档文件以及其他文件,对应设备DCIM、Pictures、Alarms、Music、Notifications、Podcasts、Ringtones、Movies、Download等目录。
分区存储在不同API上表现区别:
- 当设置targetSdk<29时,分区存储不会开启;
- 当设置targetsdk>=29时,在android10机型上,谷歌新增
android:requestLegacyExternalStorage="true"
属性可用于关闭分区存储,不过此时在android11及以上机型上面,系统会忽略 requestLegacyExternalStorage 标记,强制开启分区存储
当targetSdk设置为>=29时,在android10上,在应用非专有目录上,比如mkdir,mkdirs,createNewFile等api都会受到影响,即使是在共享目录DCIM,Pictures,Movies等下也无法使用这些api,必须使用MediaStore API创建或访问,但在android11及以上Google基于这些问题对分区存储进行了优化,这些api又可以正常使用了,Goole也已经说明:如果您的应用以 Android 10(API 级别 29)为目标平台,请停用分区存储,继续使用适用于 Android 9 及更低版本的方法来执行此操作。因此只要你的targetSdk一旦设置为29以上,务必添加android:requestLegacyExternalStorage="true"
属性。让android10及以下机型继续使用旧存储模型。
Android 10(Q) :
Android 10 中主要对共享目录进行了权限详细的划分,不再能通过绝对路径访问。
受影响的接口:
访问不同分区的方式:
- 专属目录:和以前的版本一致,可通过 File() API 访问,无需申请权限。
- 共享目录:需要通过MediaStore和Storage Access Framework API 访问,视具体情况申请权限,下面详细介绍。
其中,对共享目录的权限进行了细分:
无需申请权限的操作:
通过 MediaStore API对媒体集、文件集进行媒体/文件的添加、对自身APP 创建的 媒体/文件 进行查询、修改、删除的操作。
需要申请READ_EXTERNAL_STORAGE 权限:
通过 MediaStore API对所有的媒体集进行查询、修改、删除的操作。
调用 Storage Access Framework API :
会启动系统的文件选择器向用户申请操作指定的文件
访问方式:
Android 11 (R):
Android 11 (R) 在 Android 10 (Q) 中分区存储的基础上进行了调整
- 使用直接文件路径和原生库访问文件
为了帮助您的应用更顺畅地使用第三方媒体库,Android 11 允许您使用除 MediaStore API 之外的 API 访问共享存储空间中的媒体文件。不过,您也可以转而选择使用以下任一 API 直接访问媒体文件:
File API。
原生库,例如 fopen()。
简单来说就是,可以通过 File() , mkDir() 等API 访问有权限访问的媒体集了。