一、分区存储
1.背景
Android 11 进一步增强了平台功能,为外部存储设备上的应用和用户数据提供了更好的保护。作为这项工作的一部分,平台引入了进一步的改进,以简化向分区存储的转换。
为了让用户更好地控制自己的文件,保护用户隐私数据,并限制文件混乱情况,Android 11在分区存储基础上限制了应用访问其他应用的文件。
分区存储将存储空间分为两部分:
● 公共目录:Downloads、Documents、Pictures 、DCIM、Movies、Music、Ringtones等
■ 公共目录的文件在App卸载后,不会删除
■ 可以通过SAF、MediaStore接口访问
■ 拥有权限,也能通过路径直接访问
● 应用专属目录
■ 应用专属目录只能自己直接访问
■ App卸载,数据会清除。
2.兼容影响
当您将应用更新为以 Android 11 为目标平台后,您将无法使用requestLegacyExternalStorage,而且也没有其他标记可以提供停用分区存储。
分区存储对于App访问存储方式、App数据存放以及App间数据共享,都产生很大影响。
而Environment.getExternalStorageDirectory() 在 API Level 29 开始已被弃用,开发者应迁移至 Context#getExternalFilesDir(String), MediaStore, 或Intent#ACTION_OPEN_DOCUMENT。
二、适配
1.应用targetSdkVersion
应用targetSdkVersion >= 30,都会强制打开分区存储,同时requestLegacyExternalStorage将会无效。如果您需要对已安装的应用进行适配分区存储的数据迁移,则可以在应用更新到目标平台为Android 11版本后仍暂时保留原有的存储模式。请在应用的manifest中设置preserveLegacyExternalStorage属性为true,应用更新到android 11可以保留存储继承模式。
2.应用私有目录访问
对于运行在Android 11的应用,无论targetSdkVersion是什么都无法访问Emulated存储中的其他应用私有目录(Android/data)。SAF(Storage Access Framework)同样也禁止访问应用私有目录。
某些应用的核心用例需要访问大量的文件,如文件管理操作或备份和恢复操作。这些应用可通过执行以下操作获取“所有文件访问权限”:
● 声明 MANAGE_EXTERNAL_STORAGE 权限。
● 使用 ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION intent 操作将用户引导至一个系统设置页面,在该页面上,用户可以为您的应用启用以下选项:授予所有文件的管理权限。
● 注意:获得此权限的应用仍然无法访问属于其他应用的应用专用目录。这些目录在存储卷上显示为 Android/data/ 的子目录。
3. 直接路径访问
注意:使用直接路径和原生库保存媒体文件时,应用的性能会略有下降。请尽可能改用MediaStore API。
数据和文件存储概览 | Android 开发者 | Android Developers
Android 11 中的存储机制更新 | Android 开发者 | Android Developers
4.App运行模式
在Android 11版本上,系统会根据App targetSdkVersion决定运行模式:
● App targetSdkVersion >= 30,默认为分区存储,并且无法取消。
● App targetSdkVersion < 29,默认为分区存储,可通过requestLegacyExternalStorage更改
应用可以通过AndroidManifest.xml设置requestLegacyExternalStorage, 选择对应的方式:
● App targetSdkVersion < 29,声明了READ_EXTERNAL_STORAGE,默认Legacy Mode
● App在下列条件都成立时
■ 声明 MANAGE_EXTERNAL_STORAGE 权限。
■ 使用 ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION intent 操作将用户引导至一个系统设置页面,在该页面上,用户可以为您的应用启用以下选项:授予所有文件的管理权限。
App拥有外置存储空间Read、Write权限。但是通过Environment.isExternalStorageLegacy接口判断,返回不一定是Legacy Mode。
判断当前App运行什么模式,可以通过这个API判断:
Environment.isExternalStorageLegacy() (added in api 29);
true表示以传统的兼容方式运行,false表示以分区存储运行
5. 读写公共目录
App启动分区存储后,只能直接访问自身专属目录,所以Android 11,提供了两种访问公共目录的方法(通过MediaStore定义的Uri、通过SAF接口)
5.1通过MediaStore定义的Uri
MediaStore提供了下列几种类型的访问Uri,通过查找对应Uri数据,达到访问的目的。
下列每种类型又分为三种Uri,Internal、External、可移动存储:
●Audio
■ Internal: MediaStore.Audio.Media.INTERNAL_CONTENT_URI
content://media/internal/audio/media。
■ External: MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
content://media/external/audio/media。
■ 可移动存储: MediaStore.Audio.Media.getContentUri
content://media/<volumeName>/audio/media。
● Video
■ Internal: MediaStore.Video.Media.INTERNAL_CONTENT_URI
content://media/internal/video/media。
■ External: MediaStore.Video.Media.EXTERNAL_CONTENT_URI
content://media/external/video/media。
■ 可移动存储: MediaStore.Video.Media.getContentUri
content://media/<volumeName>/video/media。
● Image
■ Internal: MediaStore.Images.Media.INTERNAL_CONTENT_URI
content://media/internal/images/media。
■ External: MediaStore.Images.Media.EXTERNAL_CONTENT_URI
content://media/external/images/media。
■ 可移动存储: MediaStore.Images.Media.getContentUri
content://media/<volumeName>/images/media。
● File
■ MediaStore. Files.Media.getContentUri
content://media/<volumeName>/file。
● Downloads
■ Internal: MediaStore.Downloads.INTERNAL_CONTENT_URI
content://media/internal/downloads。
■ External: MediaStore.Downloads.EXTERNAL_CONTENT_URI
content://media/external/downloads。
■ 可移动存储: MediaStore.Downloads.getContentUri
content://media/<volumeName>/downloads。
5.2.通过SAF接口
SAF,即Storage Access Framework,通过选择不同的DocumentsProvider,提供给用户打开、浏览文件。在Android 11上,无法通过SAF选择外部存储器External Storage根目录、Downloads目录以及App专属目录(Android/data、Android/obb)。
Android默认提供了下列DocumentsProvider:
MediaDocumentsProvider、ExternalStorageProvider、 DownloadStorageProvider。
他们之间差异是:
6.访问应用的专属目录--私有目录
访问应用专属目录分为两种情况,第一是访问App自身专属目录,第二是访问其他App的专属目录。
6.1.App自身专属目录
Android 11获取应用专属目录
■ 获取Media接口:getExternalMediaDirs
■ 获取Cache接口:getExternalCacheDirs
■ 获取Obb接口:getObbDirs
■ 获取Data接口:getExternalFilesDirs
应用专属目录App本身可以直接访问。
6.2.其他App的专属目录
Android 11,App无法访问其他App的专属目录(Android/data)。如果需要访问其他应用专属目录数据,需要被访问者按照下列方法来提供:
6.2.1.通过SAF文件
● 共享App自定义DocumentsProvider
App自定义DocumentsProvider需要做以下步骤:
a)指定DocumentsProvider
b)DocumentsProvider实现基本接口
● 访问App通过ACTION_OPEN_DOCUMENT,启动浏览
三、文件访问限制
如果您的应用以 Android 11 为目标平台并使用存储访问框架 (SAF),则您无法再使用ACTION_OPEN_DOCUMENT和ACTION_OPEN_DOCUMENT_TREE操作访问某些目录,具体限制如下:
1.访问目录
您无法再使用ACTION_OPEN_DOCUMENT_TREE 操作来请求访问以下目录:
Downloads根目录。
内部存储根目录
2. 访问文件
您无法再使用 ACTION_OPEN_DOCUMENT_TREE 或 ACTION_OPEN_DOCUMENT操作来请求用户从以下目录中选择单独的文件:
Android/data/ 目录及其所有子目录。
Android/obb/ 目录及其所有子目录。