Android 14新特性:选择性照片和视频访问授权详解
Android 14新特性:选择性照片和视频访问授权详解
Android 14系统引入了一项重要的新特性:选择性照片和视频访问授权。这一特性在保护用户隐私的同时,也给开发者带来了新的挑战。本文将详细介绍这一新特性,并提供具体的适配方案。
Android本地读写权限变更史
Android开发者很苦。为了让大家对Android开发者的"苦"感同身受,本文将全面介绍Android系统从诞生至今,在本地读写权限方面的完整变更史。
本地读写权限指的是App拥有对手机外置公共存储空间(SD卡)读取和写入的能力。
Android 1.0
远古时代的Android系统对于权限方面是非常宽松的。那个时候据说对于本地读写功能是没有任何限制的,任何一个App都可以随意读写整个手机的公共存储空间。
Android 1.6
很明显,如此宽松的权限设计是有问题的,于是Android 1.6系统引入了WRITE_EXTERNAL_STORAGE权限。如果你想要向手机的公共存储空间写入数据,那么就得在你的应用程序的AndroidManifest.xml文件中声明这个权限才行。
Android 4.4
Android 1.6系统只是对写入公共存储空间有了限制,读取公共存储空间的文件仍然是不受限制的。那么从Android 4.4开始,Google引入了READ_EXTERNAL_STORAGE权限。如果想要读取公共存储空间的文件,就需要在AndroidManifest.xml文件中声明这个权限才行。
Android 6.0
之前的Android系统,如果你想要使用某个权限,只需要在AndroidManifest.xml文件中声明一下就行。这个规则属实有点霸王条款,因为用户无法部分同意该App申请的权限。于是在Android 6.0系统中,Google引入了运行时权限功能,某些危险程度高的权限不能再像之前那样在AndroidManifest.xml文件中声明一下就行了,而是要在App运行的过程中弹出权限申请框,只有用户同意了授权,才能使用其权限对应的功能。READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE都被划入了运行时权限的范畴。
Android 10
运行时权限机制引入之后,Android系统的隐私和安全性达到了一个新的高度,因此也让本地读写权限在相当长的一段时间里保持了比较稳定的用法。不过从Android 10系统开始,Google不满足于现状,又开始大刀阔斧地改革了,并且后面的改动频率让人瞠舌。Android 10引入了Scoped Storage机制,App被禁止使用绝对路径访问公共存储空间。这样,用户设备上的隐私信息可以得到更好的保护。而诸如手机照片、视频、音频之类的公共型资源,如果App想要访问的话,可以需要借助MediaStore API来完成。App通过MediaStore API写入照片、视频、音频等公共型资源,是不需要申请任何权限的。而App通过MediaStore API读取照片、视频、音频等公共型资源,仍需要申请READ_EXTERNAL_STORAGE权限才行。由于Scoped Storage机制变动过大,Google怕大量App来不及适配,因此提供了一个requestLegacyExternalStorage属性。将这个属性设置为true,那么App仍然可以使用绝对路径访问公共存储空间。
Android 11
给了一年的缓冲期,Google认为绝大部分应用应该都已经完成了Scoped Storage的适配,因此从Android 11开始requestLegacyExternalStorage属性将不再起作用。另外,考虑到有些文件浏览器类型的App的确需要使用绝对路径访问公共存储空间,Android 11又添加了一个MANAGE_EXTERNAL_STORAGE权限,但仅限特定确实有需求的App申请,随便申请的话可能会被Google Play商店下架。
Android 13
又过了两年,Google认为现有的本地读写权限又不够安全了。具体的原因在于,本地读写权限的划分不够精细。App只需要申请READ_EXTERNAL_STORAGE权限之后,即可访问手机公共存储空间的照片、视频、音频,用户无法以更细的颗粒度对App进行授权。于是Android 13系统废弃了READ_EXTERNAL_STORAGE权限,新增了READ_MEDIA_IMAGES、READ_MEDIA_VIDEO和READ_MEDIA_AUDIO这3个新的运行时权限,分别用于控制App对照片、视频、音频的访问。
Android 14的新特性:选择性照片和视频访问授权
为了能够更好地保护用户隐私,Google在Android 14系统中新增了选择性照片和视频访问授权功能。那么什么是选择性照片和视频访问授权呢?
在过去,当一个App申请了READ_MEDIA_IMAGES权限,如果用户选择了同意,那么该App就可以访问这台手机上所有的照片。用户是没有办法限制该App只能访问特定的某几张照片的。而Android 14新增的这个功能则允许用户选择,是一次性授权该App访问所有的照片,还是只能访问几张特定的照片。视频也是同样的道理。
虽然这个功能站在用户的角度考虑是无可厚非的,能够更好地保护用户隐私,但站在开发者的角度,由于Android系统在本地读写权限方面的历史债太多了,如果你的代码想要考虑周全所有的场景,可能需要写得相当繁琐才行。
接下来,本文将结合一个实战Demo讲解如何适配Android 14的选择性照片和视频访问授权。
选择性照片和视频访问适配
为了能够更清楚地讲解,本文将只截选与选择性照片和视频访问相关的代码进行讲解。至于Demo的完整源码,可以在文章下方找到源码的链接。
首先,Android 14推出了一个全新的运行时权限,也就是选择性照片和视频访问权限:
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
在AndroidManifest.xml中声明权限时,需要考虑Android的历史问题,完整的写法如下:
<manifest>
<!-- Devices running Android 12L (API level 32) or lower -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<!-- Devices running Android 13 (API level 33) or higher -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<!-- To handle the reselection within the app on Android 14 (API level 34) -->
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
...
</manifest>
接下来是在App运行时去请求本地读写权限的具体写法:
private val permissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { _ ->
// 处理权限请求结果
}
private fun requestPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
permissionLauncher.launch(
arrayOf(READ_MEDIA_IMAGES,
READ_MEDIA_VIDEO,
READ_MEDIA_VISUAL_USER_SELECTED)
)
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.TIRAMISU) {
permissionLauncher.launch(arrayOf(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO))
} else {
permissionLauncher.launch(arrayOf(READ_EXTERNAL_STORAGE))
}
}
最后,判断权限的请求结果需要分4种情况:
private fun checkPermissionResult() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
&& (ContextCompat.checkSelfPermission(this, READ_MEDIA_IMAGES) == PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, READ_MEDIA_VIDEO) == PERMISSION_GRANTED)
) {
// Android 13及以上完整照片和视频访问权限
} else if (
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
ContextCompat.checkSelfPermission(this, READ_MEDIA_VISUAL_USER_SELECTED) == PERMISSION_GRANTED
) {
// Android 14及以上部分照片和视频访问权限
} else if (ContextCompat.checkSelfPermission(this, READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED) {
// Android 12及以下完整本地读写访问权限
} else {
// 无本地读写访问权限
}
}
从业务流程方面,Google建议在UI层面给用户一个可操作的选项。当用户选择了部分照片和视频访问权限时,可以在界面的顶部给用户一个提示,告知下方显示的照片和视频只是用户选择授权的一部分,点击Manage按钮可以跳转到管理界面,以选择更多的照片和视频,或撤销已授权的照片和视频。
整个Demo的源码都已经上传到了GitHub上,有需要的读者可以访问下方链接进行参考:
https://github.com/guolindev/PartialAccessDemo
不适配的选择
当然,开发者可以选择不进行适配。如果App的targetSdkVersion指定在33或以下的版本,那么系统将以Android 13的兼容模式运行App,没有选择性照片和视频访问授权这个概念。如果App的targetSdkVersion指定在了34或以上的版本但没有适配选择性照片和视频访问授权功能,系统仍然能正常运行,但会有一些兼容性限制。
虽然适配的代码的确不怎么好写,但是复制粘贴总不难的吧?如果想要学习Kotlin和最新的Android知识,可以参考《第一行代码 第3版》。