Android预计在今年Q3发布androidQ的最终版本, 8月份Android发布了Beta6版本, 距离Final Release也不远了, 针对Q的适配已经迫在眉睫!
针对Q的隐私权和行为变更, 我们主要焦点还是着重于, 针对于所有应用(不论targetSdkVersion是否为Q的应用)都有影响的变更上来看
Q 针对所有应用隐私权和行为变更
分区存储
在当前的Beta5版本内, 分区存储的只应用在部分应用上, 但是, 明年无论是所有应用均需要分区存储, 所以应用需要提前确保支持分区存储.
默认情况下, 都需要通过Context.getExternalFilesDir())来获取过滤视图, 如果存在文件应用卸载时不应该删除的, 应该通过MediaProvider
存储在共享集合内(如拍照的图片), 同时, 应用请求过滤视图内的文件, 不再需要请求对应的读写权限.而访问外部存储文件, 如 /sdcard/DCIM/IMG1024.JPG
等路径文件,应用必须使用 MediaStore
,并调用 openFile()
) 等方法, 同时需要请求READ_EXTERNAL_STORAGE权限才可访问
具体适配需要参考官网的建议
针对后台启动Activity的限制
在未与用户进行交互的前提下启动Activity存在该条限制, 如果存在该场景的应用(比如闹钟, 语音, 视频电话等), 需要通过通知提醒的方式解决
增加针对后台定位的权限
AndroidQ引入了新的权限ACCESS_BACKGROUND_LOCATION
, 用来授予是否允许后台定位, 在低于Q的应用版本上, 如果原来的清单中申请了定位权限, 会自动加上ACCESS_BACKGROUND_LOCATION权限, 但是用户仍然可以通过拒绝授权, 导致后台定位失败. 这个限制主要出现在存在导航或者智能家居操作的应用中. 我们的应用中可以不做处理
设备唯一标识符
三方引用既无法通过READ_PHONE_STATE
老权限, 无法通过申请READ_PRIVILEGED_PHONE_STATE
新权限(需要系统签名)来读取deviceId, 依赖于deviceId数据上报的接口需要额外适配, Android提供推荐做法, 但是它允许用户重置标识符, 需要根据具体应用场景设计唯一标识符.另外通过WifiInfo.getMacAddress()
) 获取Mac地址的将只能获取固定的值02:00:00:00:00:00
相机和网络连接的变更
访问所有相机信息均需要获取权限, AndroidQ更改了 getCameraCharacteristics()
) 方法默认返回的信息的广度。具体而言,应用必须具有 CAMERA
权限才能访问此方法的返回值中可能包含的设备特定元数据。
非SDK接口限制
从android P开始做了非SDK接口的限制, Q版本更新了对应的非SDK接口列表, 需要测试并根据接口的不同受限情况进行相对适配
targetSdkVersion限制
当前最低目标版本需要保证在23以上
分区存储兼容性处理
为了改进当前Android手机文件夹混乱的现象, 在Android Q, Google出了分区存储的政策, 开发者将无法通过Environment.getExternalStorageState()
访问文件.
存储空间特性
所有Android设备都存在两个文件存储空间: 内部存储和外部存储.在Android早期, 内部存储代表的是内置的存储器, 而外部存储表示可移动的存储介质(譬如sd卡), 现在大部分设备无论是否存在可移动存储介质, 这两个存储空间都会存在. 而无论外部存储是否可移动, 在其API行为上, 是没有任何区别的, 当然我们可以通过以下代码去判断外部存储是否支持去读写1
2
3fun isExternalStorageWritable(): Boolean {
return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
}
在Android Q之前的手机上, 对于外部存储的文件的读写没有任何限制, 在获取外部存储的相关读写权限后, 就可以随意新建目录, 导致文件目录非常混乱
根据下图, 我们可以看到在Android Q启动了分区存储的功能后, 外部访问范围将仅限定于三个区域, 分别是过滤视图(app-specific), 共享媒体区域, 以及下载文件区域
首先, 我们可以看下这三个区域的区别
文件位置 | 需要的权限 | 访问方式 | 当应用卸载的时候, 是否会删除文件 |
---|---|---|---|
过滤视图(App-specific directory) | 无 | getExternalFilesDir | 是 |
媒体资源集合(照片, 视频, 语音) | READ_EXTERNAL_STORAGE(当访问其他应用的文件时) | MediaStore | 否 |
下载文件 | 无 | SAF(加载系统的文件管理器) | 否 |
过滤视图
通过哪些API可以访问到过滤视图?
- Context.getExternalFilesDir()
- e.g. /sdcard/Android/data/packageName/files
- Context.getExternalCacheDirs()
- e.g. /sdcard/Android/data/packageName/cache
- Context.getExternalMediaDirs()
- e.g. /sdcard/Android/media/packageName/
- Context.getObbDirs()
- e.g. /sdcard/Android/obb/packageName/
在Android P中, 应用可以通过READ_EXTERNAL_STORAGE
权限访问外部存储中所有路径文件, 而针对于过滤视图(原先叫共享存储空间, Shared Storage), 是可以不通过权限就可以直接访问读写的.
在Android Q中, 应用只可以直接访问应用自身的过滤视图, 而共享媒体资源仅可通过MediaStore来访问, 同时, 其他文件需要通过SAF进行访问, 假设直接访问过滤视图目录外的目录文件, 则会抛出异常. 这样使得文件存储更加规范, 也使文件访问变得更为安全.
MediaStore
MediaStore在Android 1就已经存在, 在Android Q 中进行了加强. 他主要用于存储用户行为生成的媒体资源文件(且这些资源文件应是应用卸载后用户仍然希望保存的文件), 当我们需要访问媒体文件的时候, 我们需要达成两个条件:
- 拥有
READ_EXTERNAL_STORAGE
权限 - 对应访问文件位于以下明确定义的媒体集合中
- 照片, 存储在
MediaStore.Images
- 音频, 存储在
MediaStore.Audio
- 视频, 存储在
MediaStore.Video
- 照片, 存储在
MediaStore Demo
1 | val resolver = context.getContentResolver() |
这里需要注意的是, 在Q版本新增了两个标记
IS_PENDING
标记用来表示标记应用具有媒体访问独占权RELATIVE_PATH
自定义指定相对路径
1 | val values = ContentValues().apply { |
另外, 在Android Q中, DATA标记已经被废弃, 在Q之前, 我们可以通过它去获取文件的绝对路径用来访问文件, 在Q乃至之后版本, 我们只能通过ContentResolver#openFileDescriptor(Uri, String)
去访问对应的文件
编辑其他应用的媒体文件
理论上, 我们无法去编辑(写, 删除操作)其他应用提供给MediaStore的媒体文件, 当我们去编辑的时候, 会抛出一个RecoverableSecurityException
异常, 我们可以通过捕获这个异常, 并请求用户授权针对该文件进行写操作
1 | try { |
相关Demo可以看下PhotoQSelector