摘要
1、本文档基于谷歌AndroidQ官方文档和一加Q版本应用兼容性整改指导
2、本文档主要对影响比较大的部分进行简单总结,内容并不全面;
3、版本号对应关系:
Android-Q = Android-10 = Api29
Android-P = Android-9.0 = Api28
一、Android Q 行为变更:版本新特性
权限 | 受影响应用 | 如何启用(影响范围) |
---|---|---|
存储权限 | 访问和共享外部存储设备中的文件的应用 | adb shell sm set-isolated-storage on(下文详述) |
定位权限 | 在后台时请求访问用户位置信息的应用 | 这种权限策略在 Android Q 上始终处于启用状态 |
从后台启动Activity | 不需要用户互动就启动 Activity 的应用 | 关闭允许系统执行后台活动开发者选项即可启用限制 |
设备标识符(deviceId) | 访问设备序列号或 IMEI 的应用 | 在搭载 Android Q 的设备上安装应用 |
无线扫描权限 | 使用 WLAN API 和 Bluetooth API 的应用 | 以 Android Q 为目标平台 |
上面的内容官方文档将这一部分内容独立于Q 行为变更:所有应用来介绍,是因为这一部分内容庞大且重要,其中最大的更新就是用户隐私权限变更. 因为无线扫描权限这种权限的变更影响较少。本文不作详述,如有涉及请查阅官方文档。
二、兼容性适配
以下对各个权限分别作介绍以及解决方法。
1、存储权限
Android Q 在外部存储设备中为每个应用提供了一个“隔离存储沙盒”(例如 /sdcard)。任何其他应用都无法直接访问您应用的沙盒文件。由于文件是您应用的私有文件,因此您不再需要任何权限即可在外部存储设备中访问和保存自己的文件。此变更可让您更轻松地保证用户文件的隐私性,并有助于减少应用所需的权限数量。
沙盒,简单而言就是应用私有专属文件夹,并且访问这个文件夹无需权限。
- 沙箱目录: /sdcard/Android/sandbox/packagename/
- 任何其他应用都无法直接访问您应用的沙盒文件。由于文件是您应用的私有文件,因此您不再需要任何>权限即可在外部存储设备中访问和保存自己的文件
- 当app卸载后,沙箱中的文件删除
谷歌官方推荐应用在沙盒内存储文件的地址为
Context.getExternalFilesDir()下的文件夹。比如要存储一张图片,则应放在Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)中。
以下将按访问的目标文件的地址介绍如何适配。
- 访问自己文件:Q中用更精细的媒体特定权限替换并取消了 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE权限,并且无需特定权限,应用即可访问自己沙盒中的文件。
- 访问系统媒体文件:Q中引入了一个新定义媒体文件的共享集合,如果要访问沙盒外的媒体共享文件,比如照片,音乐,视频等,需要申请新的媒体权限:READ_MEDIA_IMAGES,READ_MEDIA_VIDEO,READ_MEDIA_AUDIO,申请方法同原来的存储权限。
- 访问系统下载文件:对于系统下载文件夹的访问,暂时没做限制,但是,要访问其中其他应用的文件,必须允许用户使用系统的文件选择器应用来选择文件。
- 访问其他应用沙盒文件:如果你的应用需要使用其他应用在沙盒内创建的文件,请点击使用其他应用的文件,本文不做介绍。
所以请判断当应用运行在Q平台上时,取消对READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE两个权限的申请。并替换为新的媒体特定权限。
影响范围
Google 把 Android Q 上会被沙箱化条件设为 Target SDK 至少为 Q (29) 的应用或者运行 Android Q 时全新安装的应用。不符合这个条件的应用将会运行在兼容模式下,在兼容模式中应用行为大致和过去相同,以保证不会出现严重的数据丢失问题。兼容模式在应用重新安装后会被关闭。
注意:即使应用Target SDK < 29也会被沙箱化
影响点
- 沙箱化后,沙箱目录:/sdcard/Android/sandbox/packagename/,应用不能再通过 Java File API 来互相访问内部存储文件数据,应用访问 “/sdcard” 实质上访问的是你的应用的沙箱目录,可以理解为“存储重定向”; getExternalStoragePublicDirectory(),getExternalStorageDirectory(),/storage/emulated/0 都会直接映射到沙箱目录;访问这些文件路径存在自动映射,因此正常直接使用理论上都是没问题的。
- 可能存储数据丢失:当用户手机从Android Q以下版本升级到Android Q版本的时候,应用访问不了之前保存在/sdcard下面的内容。
- 沙箱空间跟过去的内部存储空间内 “Android/data/packagename” 的文件夹一样,会在应用卸载时被永久清除无法恢复,因此比如一些用户主动下载保存的文件固然不能存在沙箱空间,需要存到沙箱外面
情况描述
- 从Android10开始应用将不可直接访问外部存储(/sdcard)文件,否则抛异常。
- 在AndroidQ上运行:
targetSdkVersion<Q,没影响;
targetSdkVersion>=Q,默认启用过滤视图,应用以外的文件需要通过存储访问框架(SAF,StorageAccessFramework)读写。
解决方法
方法一、停用过滤视图,使用旧版存储模式
<manifest ... >
<!-- This attribute is "false" by default on apps targeting Android Q. -->
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
方法二、将文件存储到过滤视图中,官方推荐。
// /Android/data/com.example.androidq/files/Documents
File dir = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
优点:不用申请读写权限;
缺点:随应用卸载而删除;
方法三、使用存储访问框架(SAF),由用户指定要读写的文件。
这个功能Android 4.4(API: 19)就有,参考官方文档。
方法四、获取用户指定的某个目录的读写权限
从Android5.0(Api 21)开始就有,官方文档。
步骤
1. 申请目录的访问权限
会打开系统的文件目录,由用户自己选择允许访问的目录,不用申请WRITE/READ_EXTERNAL_STORAGE
权限。
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.