前言
本文基于 Android 12 开发者预览版 2 中进行介绍新版本中的部分特性。
时间轴、里程碑和更新:
1. 获取 Android 12
获取最新 Android 12 的方式有两种,一为你已拥有安装了 Android 11 的 Google Pixel 设备,或者 Android 模拟器。本文介绍的方式为如何通过模拟器设置 Android 12 的 SDK。
在 Android Studio 中,依次点击 Tools > SDK Manager。
在 SDK Tools 标签页中,选择最新版 Android 模拟器,然后点击 OK。如果尚未安装最新版本,则此操作会安装最新版本。
-
在 Android Studio 中,依次点击 Tools > AVD Manager,然后按照说明创建新的 Android 虚拟设备 (AVD)。
请务必选择 Pixel 3、Pixel 3a、Pixel 4、Pixel 4a 或 Pixel 5 设备定义以及 64 位 Android 12 模拟器系统映像。请注意,Android 12 不支持 32 位 Android 模拟器系统映像。如果您尚未安装与您的设备定义匹配的 Android 12 系统映像,请点击 Release Name 旁边的 Download 下载该映像。
在 AVD Manager 中返回虚拟设备列表,然后双击您的 Android 12 虚拟设备以启动它。
2. API 变更
Android 12 面向开发者引入了一些出色的新功能和 API。以下几部分内容可帮助您了解适用于您的应用的功能并开始使用相关 API。
2.1 GIF 和 WebP 的原生 ImageDecoder 支持
在 Android 12 中,NDK ImageDecoder
API 已进行了扩展,可以对 GIF 和 WebP 文件格式的图片的所有帧和时间数据进行解码。在 Android 11 中引入该 API 时,该 API 仅从这些格式的动画中解码第一张图片。
使用 ImageDecoder
(而非第三方库)可进一步缩减 APK 大小,并从未来与安全性和性能相关的更新中受益。
2.2 支持圆角
许多现代设备的屏幕采用圆角设计,这彰显了简洁时尚的风格,但也为应用开发者带来一些额外的问题。要在这些设备上提供出色的用户体验,开发者需要将这些圆角纳入考虑,调整圆角附近的界面元素,防止出现裁切的情况。
为了解决这个问题,Android 12 引入了新的 API,便于您查询圆角及其详情。RoundedCorner 提供了圆角半径、中心点等信息。可以通过 Display.getRoundedCorner() 来获取每个圆角的详尽信息。
还可以调用 WindowInsets.getRoundedCorner() 来获取基于应用边界的圆角位置等信息。如此一来,你就可以根据需要灵活调整界面元素和内容的位置。
2.3 更易用的模糊、色彩滤镜等特效
在 Android 12 中,可以更轻松地将常用图形效果应用于视图和渲染结构上。可以使用 RenderEffect 将模糊、色彩滤镜等特效应用于任何 RenderNode。可以将这些特效组合为连锁特效 (即组成内部和外部特效),也可以混用这些特效。还可以通过调用 View.setRenderEffect(RenderEffect) 将特效直接应用于视图 (View,因为其基于 RenderNode)。
view.setRenderEffect(RenderEffect.createBlurEffect(radiusX, radiusY, SHADER_TILE_MODE))
有了这个功能,在模糊 ImageView 时,就不必进行获取位图数据、处理图像、创建新 Bitmap,再将其设置回 ImageView 这些操作了。RenderEffect 利用了现有的渲染流水线,最大限度地减少了额外的计算量。
还可以使用新的 Window.setBackgroundBlurRadius() API 为窗口背景创建雾面玻璃效果。这个 API 可以设置模糊半径,以调整雾面密度和范围,平台只会对您的应用窗口边框内的背景内容应用模糊效果。还可以使用 blurBehindRadius 来模糊窗口后面的所有内容,从而为浮动窗口营造出深度效果。
3. 影响应用的行为变更
3.1 针对所有应用的行为变更
以下行为变更将影响在 Android 12 上运行的所有应用,无论采用哪种 targetSdkVersion
都不例外。
3.1.1 对 MAC 地址的限制
Android 12 进一步限制了所有非系统应用对设备的 MAC 地址的访问。
相关 API 会返回空值或占位值,具体取决于应用的 Target SDK 版本:
- 如果应用以 Android 12 为目标平台,则该 API 会返回 null。
- 如果应用以 Android 11 或更低版本为目标平台,则该 API 将返回硬编码占位值:
02:00:00:00:00:00
开发者应使用 ConnectivityManager
,而不是较低级别的 API,例如 NetworkInterface
、getifaddrs()
。开发者在其代码中调用 NetworkInterface.getHardwareAddress()
时,logcat 输出会显示:CompatibilityChangeReporter: Compat change id reported: 170188668;
开发者可以使用名为 RETURN_NULL_HARDWARE_ADDRESS
的兼容性标志切换 NetworkInterface.getHardwareAddress()
的行为:在启用时返回 null,或在停用时返回 02:00:00:00:00:00
。
3.1.2 应用覆盖控制
Android 的系统警告框允许一个应用在其他活动应用之上覆盖显示一个图层,以帮助应用提醒用户关注重要操作。不过由于这些窗口会中断用户操作,因此应用需要请求权限才能显示这些内容。在 Android 12 中,可以控制是否允许在自己的内容上显示这些覆盖图层。在声明新权限后,你的应用可以调用 Window#setHideOverlayWindows(),以表明在你的应用窗口可见时应隐藏所有 TYPE_APPLICATION_OVERLAY 窗口,比如可以在交易确认流程等敏感界面中如此操作。
3.1.3 应用无法关闭系统对话框
为了加强用户与应用和系统互动时的控制,从 Android 12 开始,弃用了 ACTION_CLOSE_SYSTEM_DIALOGS
intent 操作。除了一些特殊情况之外,当应用尝试调用包含此操作的 intent 时,系统会基于应用的目标 SDK 版本执行以下操作之一:
如果应用以 Android 12 为目标平台,则会发生 SecurityException。
如果应用以 Android 11(API 级别 30)或更低版本为目标平台,则系统不会执行 intent,并且 Logcat 中会显示以下消息:
E ActivityTaskManager Permission Denial: \
android.intent.action.CLOSE_SYSTEM_DIALOGS broadcast from \
com.package.name requires android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, \
dropping broadcast.
3.2 针对以 Android 12 为目标平台的应用
3.2.1 更安全的导出组件
包含 intent 过滤器的应用组件必须显式设置 android:exported
属性,如 Activity、Service、Broadcast Receive。 未设置该属性的应用无法安装
在 Android 12 上。
错误信息:
Installation did not succeed.
The application could not be installed: INSTALL_FAILED_VERIFICATION_FAILURE
List of apks:
[0] '.../build/outputs/apk/debug/app-debug.apk'
Installation failed due to: 'null'
正确示范:
<service android:name="com.example.app.backgroundService"
android:exported="false">
<intent-filter>
<action android:name="com.example.app.START_BACKGROUND" />
</intent-filter>
</service>
3.2.2 待处理 intent 必须声明可变性
如果你的应用以 Android 12 为目标平台,则必须为你的应用创建的每个 PendingIntent
对象指定可变性。这项额外的要求可提高应用的安全性。
如需声明特定 PendingIntent
对象是否可变,请分别使用 PendingIntent.FLAG_MUTABLE
或 PendingIntent.FLAG_IMMUTABLE
标志。如果你的应用试图在不设置任何可变标志的情况下创建 PendingIntent
对象,系统会抛出 IllegalArgumentException
,并在 Logcat 中显示以下消息:
PACKAGE_NAME: Targeting S+ (version 10000 and above) requires that one of \
FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if \
some functionality depends on the PendingIntent being mutable, e.g. if \
it needs to be used with inline replies or bubbles.
尽可能创建不可变的待处理 intent
在大多数情况下,你的应用应创建不可变的 PendingIntent
对象,如以下代码段所示。如果 PendingIntent
对象不可变,则应用无法修改 intent 来调整调用 intent 的结果。
PendingIntent pendingIntent = PendingIntent.getActivity(
getApplicationContext(),
REQUEST_CODE,
intent,
PendingIntent.FLAG_IMMUTABLE // flags
);
然而,某些应用需要创建可变的 PendingIntent
对象:
- 通知中的直接回复操作需要变更与回复关联的
PendingIntent
对象中的剪辑数据。通常,您可以通过将FILL_IN_CLIP_DATA
作为标志传递给fillIn()
的方法请求此变更。 - 如果您的应用使用
PendingIntent
将对话放在气泡中,则 intent 应该可变,以便系统可以应用正确的标志,例如FLAG_ACTIVITY_MULTIPLE_TASK
和FLAG_ACTIVITY_NEW_DOCUMENT
。
如果您的应用创建了可变的 PendingIntent
对象,强烈建议您使用显式 intent 并填写 ComponentName
。如此一来,每当另一个应用调用 PendingIntent
并将控制权传回您的应用时,应用中的相同组件都会启动。
3.2.3 前台服务启动限制
以 Android 12 为目标平台的应用在后台运行时无法再启动前台服务,但一些特殊情况除外。如果应用在后台运行时尝试启动前台服务,并且前台服务不符合任何特殊情况,则系统会抛出IllegalStateException
。
4. 关于APP 适配
基于对 Android 12 的调研以及还呗当前的环境配置,总结出还呗可能面临的一些适配问题
我司 App 当前环境配置:
compileSdkVersion = 27
targetSdkVersion = 27
由于当前的目标版本为27,小于 Android 12 的目标版本,因此目前只需要关注所有应用的行为变更
,如MAC 地址的限制
、禁止使用ACTION_CLOSE_SYSTEM_DIALOGS
的 intent 操作。
如果未来计划升级目标版本至 31,则需关注以 Android 12 为目标平台的改动,如清单文件中申明了intent-filter
的 Activity、Service,必须设置android:exported
,我们目前在 APP 启动页、短链唤起页等使用了 intent 过滤器;以及在集成推送服务时,使用到的PendingIntent
也需要适配,详细可参考官方文档。