Android11适配

1.分区存储

1.1.背景

Android 11 进一步增强了平台功能,为外部存储设备上的应用和用户数据提供了更好的保护。作为这项工作的一部分,平台引入了进一步的改进,以简化向分区存储的转换。
为了让用户更好地控制自己的文件,保护用户隐私数据,并限制文件混乱情况,Android 11在分区存储基础上限制了应用访问其他应用的文件。

分区存储将存储空间分为两部分:

●  公共目录:Downloads、Documents、Pictures 、DCIM、Movies、Music、Ringtones等

    ■ 公共目录的文件在App卸载后,不会删除
    ■ 可以通过SAF、MediaStore接口访问
    ■ 拥有权限,也能通过路径直接访问
●  应用专属目录
    ■ 应用专属目录只能自己直接访问
    ■ App卸载,数据会清除。

1.2兼容影响

当您将应用更新为以 Android 11 为目标平台后,您将无法使用requestLegacyExternalStorage,而且也没有其他标记可以提供停用分区存储。
分区存储对于App访问存储方式、App数据存放以及App间数据共享,都产生很大影响。
而Environment.getExternalStorageDirectory() 在 API Level 29 开始已被弃用,开发者应迁移至 Context#getExternalFilesDir(String), MediaStore, 或Intent#ACTION_OPEN_DOCUMENT。

1.3 适配

1.3.1应用targetSdkVersion

应用targetSdkVersion >= 30,都会强制打开分区存储,同时requestLegacyExternalStorage将会无效。如果您需要对已安装的应用进行适配分区存储的数据迁移,则可以在应用更新到目标平台为Android 11版本后仍暂时保留原有的存储模式。请在应用的manifest中设置preserveLegacyExternalStorage属性为true,应用更新到android 11可以保留存储继承模式。

1.3.2应用私有目录访问

对于运行在Android 11的应用,无论targetSdkVersion是什么都无法访问Emulated存储中的其他应用私有目录(Android/data)。SAF(Storage Access Framework)同样也禁止访问应用私有目录。
某些应用的核心用例需要访问大量的文件,如文件管理操作或备份和恢复操作。这些应用可通过执行以下操作获取“所有文件访问权限”:

●  声明 MANAGE_EXTERNAL_STORAGE 权限。
●  使用 ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION intent 操作将用户引导至一个系统设置页面,在该页面上,用户可以为您的应用启用以下选项:授予所有文件的管理权限。

●  注意:获得此权限的应用仍然无法访问属于其他应用的应用专用目录。这些目录在存储卷上显示为 Android/data/ 的子目录。

1.3.3 直接路径访问

注意:使用直接路径和原生库保存媒体文件时,应用的性能会略有下降。请尽可能改用MediaStore API。

数据和文件存储概览  |  Android 开发者  |  Android Developers
Android 11 中的存储机制更新  |  Android 开发者  |  Android Developers

1.3.4App运行模式

在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表示以分区存储运行

1.3.5 读写公共目录

App启动分区存储后,只能直接访问自身专属目录,所以Android 11,提供了两种访问公共目录的方法(通过MediaStore定义的Uri、通过SAF接口)

1.3.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。

1.3.5.1.1. 获取所有的Volume

1.3.5.2.通过SAF接口
SAF,即Storage Access Framework,通过选择不同的DocumentsProvider,提供给用户打开、浏览文件。在Android 11上,无法通过SAF选择外部存储器External Storage根目录、Downloads目录以及App专属目录(Android/data、Android/obb)。

Android默认提供了下列DocumentsProvider:
MediaDocumentsProvider、ExternalStorageProvider、 DownloadStorageProvider。
他们之间差异是:

1.3.6访问应用的专属目录

访问应用专属目录分为两种情况,第一是访问App自身专属目录,第二是访问其他App的专属目录。
1.3.6.1.App自身专属目录
   Android 11获取应用专属目录
   ■  获取Media接口:getExternalMediaDirs
   ■  获取Cache接口:getExternalCacheDirs
   ■  获取Obb接口:getObbDirs
   ■  获取Data接口:getExternalFilesDirs
   应用专属目录App本身可以直接访问。
1.3.6.2.其他App的专属目录
Android 11,App无法访问其他App的专属目录(Android/data)。如果需要访问其他应用专属目录数据,需要被访问者按照下列方法来提供:
1.3.6.2.1.通过SAF文件
●  共享App自定义DocumentsProvider
    App自定义DocumentsProvider需要做以下步骤:
    a)指定DocumentsProvider

1.3.6.2.其他App的专属目录
Android 11,App无法访问其他App的专属目录(Android/data)。如果需要访问其他应用专属目录数据,需要被访问者按照下列方法来提供:
1.3.3.2.1.通过SAF文件
●  共享App自定义DocumentsProvider
    App自定义DocumentsProvider需要做以下步骤:
    a)指定DocumentsProvider

   b)DocumentsProvider实现基本接口

●  访问App通过ACTION_OPEN_DOCUMENT,启动浏览

1.3.3.2.2.共享App实现FileProvider

1.3.3.2.3.App自定义私有Provider

1.3.7.App Scopted Storage,访问权限总结
App访问不同目录的权限总结如下:

1.3.8.直接路径访问
Android 11上,App可以直接通过路径访问拥有权限的文件。例如,可以通过路径访问自己通过MediaStore新建的Images。

因为现在分区存储公共区域,是基于FUSE来实现,通过直接路径访问会经过下列路程:访问者 →  FUSE →  KERNEL---->MediaProvider(得到真实数据)--->KERNEL →  FUSE→访问者,比之前SDCARDFS多了几个步骤,所以会导致一些性能问题。建议通过MediaStore访问。

1.3.9.宽泛权限
Android 11,提供了两种宽泛权限,需要注意的是这两种宽泛权限是无法访问其他应用的专属目录:
●  MANAGE_EXTERNAL_STORAGE
    App拥有此权限,能够读写公共区域内所有文件,并且可以访问MediaStore.Files里面的所有文件。此权限能够满足清理、手机搬家、杀毒、文件管理这些类型应用需求。
    App可以通过下列方式申请:

配置文件中先添加该权限

●  System Gallery Role
    Gallery Role只能是预装的系统应用,通过系统配置才能成为Gallery Role。拥有Gallery Role,通过MediaStore读写多媒体文件不用弹框用户交互

1.3.10.应用卸载
●  如果App在AndroidManifest.xml中声明:android:hasFragileUserData="true"
    卸载应用会有提示是否保留App数据:

●  App存放到公共目录下的文件,卸载后,如果需要修改,需要用户重新授予权限

1.3.11.App数据迁移
App打开分区存储,会涉及到数据的迁移,不然会导致旧数据无法使用。可以从下面几方面着手数据迁移:
●  App对于可以存放到公共目录的文件,可以通过MediaStore接口存放到对应类型的公共目录中。
●  对于私有数据,可以存放到App私有目录。
●  迁移后数据的共享访问
    ■  对于存放到公共目录的文件,其他App可以通过MediaStore访问。
    ■  对于无法存放在公共目录文件,可以放置在私有目录,通过Uri共享给其他App访问。

1.3.12.MediaStore Queries
在使用MediaStore进行query动作的时候,使用Projection时,Column Name要在MediaStore中定义好的。

1.3.13.新建测试使用可移动存储
如果一个设备没有可移动的存储,可以使用下面的方法新建虚拟存储设备:
●  adb shell sm set-virtual-disk true
●  在设置 -> 存储 -> Virtual SD,进行初始化

1.4.规范愿景
我们希望三方应用,尤其是TOP应用,能够按照分区存储的规范,将用户数据(例如图片、视频、音频等)保存在公共目录,把应用数据保存在SDCARD私有目录,以更好地保护外部存储上的应用和用户数据。而Google正在更新 Google Play 政策,以确保应用只在其真正需要获取位置信息时才请求授权。

2.1.2应用缓存

1 背景
在Android 11上,应用默认不能删除其他应用的缓存文件,即使申请了MANAGE_EXTERNAL_STORAGE权限。
Google官网特性介绍:
Android 11 中的存储机制更新  |  Android 开发者  |  Android Developers
2 兼容性影响
文件管理类,清理类或其他具有缓存清理功能应用,清除其他应用缓存功能失效。
3 适配指导
1 使用intent action - ACTION_MANAGE_STORAGE 检查可用存储空间大小。
2 如果可用的存储空间不足,使用 intent action —ACTION_CLEAR_APP_CACHE 呈现UI界面让用户确认后,触发所有应用的缓存清理。
注意:执行 ACTION_CLEAR_APP_CACHE 触发的缓存清理,会清理所有应用的缓存,同时大量的IO操作也会加剧电量消耗,如非必要,请不要使用。

2.1.3文件访问限制
1 背景
如果您的应用以 Android 11 为目标平台并使用存储访问框架 (SAF),则您无法再使用ACTION_OPEN_DOCUMENT和ACTION_OPEN_DOCUMENT_TREE操作访问某些目录,具体限制如下:
1 访问目录
您无法再使用ACTION_OPEN_DOCUMENT_TREE 操作来请求访问以下目录:
Downloads根目录。
设备制造商认为可靠的各个 SD 卡根目录,无论该卡是模拟卡还是可移除的卡。
内部存储根目录
2 访问文件
您无法再使用 ACTION_OPEN_DOCUMENT_TREE 或 ACTION_OPEN_DOCUMENT操作来请求用户从以下目录中选择单独的文件:
Android/data/ 目录及其所有子目录。
Android/obb/ 目录及其所有子目录。

2 兼容性影响
如果应用指定AndroidR为运行平台,则不再能使用SAF访问上述指出的目录,可能导致您的业务逻辑异常。
3 适配指导
执行以下操作来确认行为变更是否已对应用生效:
1 将targetSdkVersion指向Android 11
2 确保已经打开RESTRICT_STORAGE_ACCESS_FRAMEWORK 兼容性开关(使用方法见文档兼容性调试工具部分) 。
3 使用 intent action - ACTION_OPEN_DOCUMENT_TREE ,检查Downloads目录是否显示并呈灰显状态。
4 使用intent action - ACTION_OPEN_DOCUMENT检查Android/data/和Android/obb/目录是否都不显示。

2.1.4存储权限变更
1 背景
Android 11 引入了与存储权限相关的以下变更。
1 不管应用的目标 SDK 版本是什么,以下变更均会在 Android 11 中生效:
●  存储运行时权限已重命名为文件和媒体。
●  如果应用未选择停用分区存储,并且请求 READ_EXTERNAL_STORAGE 权限,则用户会看到不同于 Android 10 的对话框。该对话框会指示应用正在请求访问相册和多媒体。如下图所示

在系统设置的设置 > 隐私 > 权限管理器 > 文件和媒体 页面中,用户可以查看已授予权限READ_EXTERNAL_STORAGE应用,应用会列在允许存储所有文件下。
注意:如果您的应用以Android 11 为目标运行平台,上述允许存储所有文件代表的是对文件的只读权限。

2 以 Android 11 为目标平台
如果应用以 Android 11 为目标平台,则WRITE_EXTERNAL_STORAGE 权限和 WRITE_MEDIA_STORAGE 特许权限将不再提供任何其他访问权限。
2 兼容性影响
1 存储运行时权限UI发生变更。
2 WRITE_EXTERNAL_STORAGE 权限和 WRITE_MEDIA_STORAGE 在targetSdkVersion 指定为30时,发生变更。

2.1.5所有文件访问
1背景
有些应用主要功能就是访问手机存储文件,例如文件管理器、备份&恢复出厂操作。在Android 11 版本上,需要通过声明MANAGE_EXTERNAL_STORAGE权限来获取“Allowed for all files(允许存储所有文件)”权限,进行功能实现。
此权限被授予后,拥有以下权限:
1.“共享存储”上的所有文件的读写权限
共享存储说明:
共享存储空间概览  |  Android 开发者  |  Android Developers
2. MediaStore.Files表内容
注意:即便授予了所有文件访问权限,应用也不能获取其他app的应用专属的文件。
应用专属目录:
访问应用专属文件  |  Android 开发者  |  Android Developers
2兼容性影响
文件管理类应用或其他需要对较多存储文件进行扫描和处理的应用,可能会功能失效。
3 适配指导
Google适配指导:
Android 11 中的存储机制更新  |  Android 开发者  |  Android Developers

1 AndroidManifest.xml中声明MANAGE_EXTERNAL_STORAGE权限。
2 使用intent action - ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION 跳转到系统设置页面,引导用户开启“Allowed for all files(允许存储所有文件)”权限。

相关推荐

  1. Android 14 应用指南

    2024-04-26 08:04:03       50 阅读
  2. T527 Android13遥控

    2024-04-26 08:04:03       51 阅读
  3. Android 10 】隐私权限变更

    2024-04-26 08:04:03       47 阅读
  4. Unity Android(十) Android14系统

    2024-04-26 08:04:03       31 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-26 08:04:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-26 08:04:03       100 阅读
  3. 在Django里面运行非项目文件

    2024-04-26 08:04:03       82 阅读
  4. Python语言-面向对象

    2024-04-26 08:04:03       91 阅读

热门阅读

  1. 每天学习一个Linux命令之bzip2

    2024-04-26 08:04:03       30 阅读
  2. Elasticsearch 详细介绍和经典应用

    2024-04-26 08:04:03       34 阅读
  3. VSCode 常用配置

    2024-04-26 08:04:03       28 阅读
  4. 使用rust学习基本算法(三)

    2024-04-26 08:04:03       27 阅读
  5. js中Symbol值的强制类型转换

    2024-04-26 08:04:03       39 阅读
  6. vue3 子组件实现v-model用法

    2024-04-26 08:04:03       30 阅读
  7. Apache Flink 中作业图与执行图的深入解析

    2024-04-26 08:04:03       31 阅读
  8. [晕事]今天做了件晕事30, perf

    2024-04-26 08:04:03       33 阅读