Android 12 平台包含可能影响您的应用的行为变更。无论 targetSdkVersion
如何,以下行为变更均适用于在 Android 12 上运行的所有应用。您应该测试您的应用,并根据需要进行修改以妥善支持这些变更。
此外,请务必查看 仅影响以 Android 12 为目标平台的应用的行为变更列表。
用户体验
拉伸过度滚动效果
在运行 Android 12 及更高版本的设备上,过度滚动事件的视觉行为会发生变化。
在 Android 11 及更低版本中,过度滚动事件会导致视觉元素发光;在 Android 12 及更高版本中,视觉元素会在拖动事件中拉伸并弹回,并在快速滑动事件中快速滑动并弹回。
如需了解详情,请参阅动画滚动手势指南。
应用启动画面
如果您之前在 Android 11 或更低版本中实现了自定义启动画面,则需要将应用迁移到 SplashScreen
API,以确保它从 Android 12 开始能正确显示。不迁移应用将导致应用启动体验下降或出现意外情况。
如需了解说明,请参阅将现有启动屏幕实现迁移到 Android 12。
此外,从 Android 12 开始,系统始终对所有应用的冷启动和热启动应用新的Android 系统默认启动画面。默认情况下,此系统默认启动画面是使用应用的启动器图标元素和主题的windowBackground
(如果它是一种颜色)构建的。
如需了解更多详情,请参阅启动画面开发者指南。
Web Intent 解析
从 Android 12(API 级别 31)开始,通用 Web Intent 仅在您的应用获得该 Web Intent 中包含的特定域名的批准后,才能解析为应用中的 Activity。如果您的应用未获得该域名的批准,则该 Web Intent 将改为解析为用户的默认浏览器应用。
应用可以通过执行以下任一操作获得此批准:
使用Android 应用链接验证域名。
对于以 Android 12 或更高版本为目标平台的应用,系统会更改自动验证应用 Android 应用链接的方式。在您的应用的Intent 过滤器中,检查您是否包含
BROWSABLE
类别并支持https
方案。在 Android 12 或更高版本上,您可以手动验证应用的 Android 应用链接,以测试此更新的逻辑如何影响您的应用。
在系统设置中请求用户将您的应用与域名相关联。
如果您的应用调用 Web Intent,请考虑添加提示或对话框,要求用户确认该操作。
手势导航的沉浸模式改进
Android 12 整合了现有行为,使用户在沉浸模式下执行手势导航命令变得更容易。此外,Android 12 还为粘性沉浸模式提供了向后兼容行为。
Display#getRealSize 和 getRealMetrics:弃用和限制
Android 设备有多种不同的外形规格,例如大屏设备、平板电脑和可折叠设备。为了针对每种设备适当地渲染内容,您的应用需要确定屏幕或显示屏尺寸。随着时间的推移,Android 提供了不同的 API 来检索此信息。在 Android 11 中,我们引入了 WindowMetrics
API 并弃用了以下方法:
在 Android 12 中,我们继续建议使用 WindowMetrics
,并弃用以下方法:
为了缓解应用使用 Display API 检索应用边界的行为,Android 12 限制了这些 API 对无法完全调整大小的应用返回的值。这可能会影响使用此信息与 MediaProjection
配合使用的应用。
应用应使用 WindowMetrics
API 查询其窗口的边界,并使用 Configuration.densityDpi
查询当前密度。
为了与旧版 Android 保持更广泛的兼容性,您可以使用 Jetpack WindowManager
库,它包含一个支持 Android 4.0(API 级别 14)及更高版本的 WindowMetrics
类。
如何使用 WindowMetrics 的示例
首先,确保您应用的 Activity 可完全调整大小。
Activity 应依赖于 Activity 上下文中的 WindowMetrics
来完成任何与界面相关的工作,特别是 WindowManager.getCurrentWindowMetrics()
或 Jetpack 的 WindowMetricsCalculator.computeCurrentWindowMetrics()
。
如果您的应用创建 MediaProjection
,则边界必须正确调整大小,因为投影会捕获投影应用运行所在的显示分区。
如果应用可完全调整大小,则 Activity 上下文会按如下方式返回正确的边界:
Kotlin
val projectionMetrics: WindowMetrics = activityContext .getSystemService(WindowManager::class.java).maximumWindowMetrics
Java
WindowMetrics projectionMetrics = activityContext .getSystemService(WindowManager.class).getMaximumWindowMetrics();
如果应用无法完全调整大小,则必须从 WindowContext
实例查询,并使用 WindowManager.getMaximumWindowMetrics()
或 Jetpack 方法 WindowMetricsCalculator.computeMaximumWindowMetrics()
检索 Activity 边界的 WindowMetrics
。
Kotlin
val windowContext = context.createWindowContext(mContext.display!!, WindowManager.LayoutParams.TYPE_APPLICATION, null) val projectionMetrics = windowContext.getSystemService(WindowManager::class.java) .maximumWindowMetrics
Java
Context windowContext = context.createWindowContext(mContext.getDisplay(), WindowManager.LayoutParams.TYPE_APPLICATION, null); WindowMetrics projectionMetrics = windowContext.getSystemService(WindowManager.class) .getMaximumWindowMetrics();
多窗口模式下的所有应用
Android 12 将多窗口模式设为标准行为。
在大屏设备(sw >= 600dp)上,平台支持所有应用在多窗口模式下运行,无论应用配置如何。如果 resizeableActivity="false"
,应用会在必要时进入兼容模式以适应显示尺寸。
在小屏设备(sw < 600dp)上,系统会检查 Activity 的 minWidth
和 minHeight
,以确定该 Activity 是否可以在多窗口模式下运行。如果 resizeableActivity="false"
,无论最小宽度和高度如何,应用都将无法在多窗口模式下运行。
如需了解详情,请参阅多窗口支持。
大屏设备上的相机预览
相机应用通常假设设备方向与相机预览的宽高比之间存在固定关系。但大屏设备外形规格(例如可折叠设备)以及多窗口和多显示屏等显示模式对这一假设构成了挑战。
在 Android 12 上,请求特定屏幕方向且不可调整大小 (resizeableActivity="false"
) 的相机应用会自动进入内嵌竖屏模式,这可确保相机预览的正确方向和宽高比。在可折叠设备和其他具有相机硬件抽象层 (HAL) 的设备上,相机输出会进行额外旋转以补偿相机传感器方向,并且相机输出会裁剪以匹配应用相机预览的宽高比。裁剪和额外旋转可确保无论设备方向以及设备的折叠或展开状态如何,都能正确呈现相机预览。
前台服务通知的 UX 延迟
为了缩短前台服务的运行时间并提供流畅体验,运行 Android 12 或更高版本的设备可以将前台服务通知的显示延迟 10 秒(少数情况除外)。此变更可让短期任务有机会在通知显示之前完成。
性能
受限应用待机分区
Android 11(API 级别 30)引入了受限分区作为应用待机分区。从 Android 12 开始,此分区默认处于活动状态。受限分区在所有分区中具有最低优先级(和最高限制)。分区按优先级从高到低依次为:
- 活跃:应用当前正在使用或最近使用过。
- 工作集:应用正在正常使用中。
- 常用:应用经常使用,但不是每天都使用。
- 不常用:应用不经常使用。
- 受限:应用消耗大量系统资源,或可能出现不良行为。
系统会根据您的应用行为以及使用模式来决定是否将您的应用放入受限分区。
如果您的应用更负责任地使用系统资源,则不太可能被放置在受限分区中。此外,如果用户直接与您的应用互动,系统会将您的应用放置在限制较少的分区中。
检查您的应用是否在受限分区中
要检查系统是否已将您的应用放置到受限分区,请调用 getAppStandbyBucket()
。如果此方法的返回值为 STANDBY_BUCKET_RESTRICTED
,则您的应用位于受限分区中。
测试受限分区行为
要测试当系统将您的应用放置到受限分区时,您的应用会如何表现,您可以手动将您的应用移动到该分区。为此,请在终端窗口中运行以下命令:
adb shell am set-standby-bucket PACKAGE_NAME restricted
前台位置信息和省电模式
从 Android 12 开始,即使屏幕关闭,只要省电模式处于活动状态,前台位置信息(包括来自前台服务的位置信息)就可以继续传送。
之前,当屏幕关闭时,省电模式会停止位置信息更新。此变更可为用户延长电池续航时间,这意味着开发者无需再要求用户停用省电模式以确保位置信息传送。
通过前台服务请求位置信息的应用应执行以下步骤:
- 调用
getLocationPowerSaverMode()
,检查设备位置信息功能在省电模式处于活动状态时的行为方式。 - 如果返回
LOCATION_MODE_FOREGROUND_ONLY
,则在省电模式开启且屏幕关闭时,您的应用将在前台或运行前台服务时继续接收位置信息更新。
安全和隐私
大致位置
在运行 Android 12 或更高版本的设备上,用户可以请求您的应用仅访问大致位置信息。
如果您的应用请求 ACCESS_FINE_LOCATION
运行时权限,您还应请求 ACCESS_COARSE_LOCATION
权限,以处理用户授予应用大致位置信息访问权限的情况。您应在单个运行时请求中包含这两个权限。
系统权限对话框包含以下用户选项,如图 1 所示:
- 精确:提供对精确位置信息的访问权限。
- 大致:仅提供对大致位置信息的访问权限。
麦克风和摄像头开关
运行 Android 12 或更高版本的受支持设备允许用户通过按单个切换选项来为设备上的所有应用启用和禁用摄像头和麦克风访问权限。用户可以从快捷设置(如图 1 所示)或系统设置中的“隐私”屏幕访问可切换选项。
详细了解这些开关,以及如何检查您的应用是否遵循有关 CAMERA
和 RECORD_AUDIO
权限的最佳实践。
麦克风和摄像头指示器
在运行 Android 12 或更高版本的设备上,当应用访问麦克风或摄像头时,状态栏中会显示一个图标。
详细了解这些指示器,以及如何检查您的应用是否遵循有关 CAMERA
和 RECORD_AUDIO
权限的最佳实践。
权限软件包可见性
在运行 Android 12 或更高版本的设备上,以 Android 11(API 级别 30)或更高版本为目标平台并调用以下方法之一的应用会收到一组经过滤的结果,具体取决于应用对其他应用的软件包可见性:
BouncyCastle 实现已移除
Android 12 移除了许多之前已弃用的BouncyCastle 加密算法实现,包括所有 AES 算法。系统改为使用这些算法的Conscrypt 实现。
如果出现以下任一情况,此变更会影响您的应用:
- 您的应用使用 512 位密钥大小。Conscrypt 不支持此密钥大小。如有必要,请更新您应用的加密逻辑以使用不同的密钥大小。
您的应用使用无效密钥大小和
KeyGenerator
。与 BouncyCastle 相比,Conscrypt 的KeyGenerator
实现对密钥参数执行了额外的验证。例如,Conscrypt 不允许您的应用生成 64 位 AES 密钥,因为 AES 仅支持 128 位、192 位和 256 位密钥。BouncyCastle 允许生成无效大小的密钥,但如果这些密钥与
Cipher
一起使用,则会失败。Conscrypt 会更早地失败。您使用非 12 字节的大小初始化伽罗瓦/计数器模式 (GCM) 密码。Conscrypt 的
GcmParameterSpec
实现要求初始化为 12 字节,这是 NIST 推荐的。
剪贴板访问通知
在 Android 12 及更高版本上,当应用首次调用 getPrimaryClip()
以访问其他应用中的剪贴数据时,系统会显示一条 Toast 消息,通知用户此剪贴板访问。
Toast 消息中的文本采用以下格式:应用名称 已从您的剪贴板粘贴。
剪辑描述中的文本信息
在 Android 12 及更高版本上,getPrimaryClipDescription()
可以检测以下详细信息:
- 样式化文本,使用
isStyledText()
。 - 不同的文本分类(例如网址),使用
getConfidenceScore()
。
应用无法关闭系统对话框
为了改进用户与应用和系统交互时的控制,ACTION_CLOSE_SYSTEM_DIALOGS
Intent 操作自 Android 12 起已弃用。除少数特殊情况外,当您的应用尝试调用包含此操作的 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.
例外情况
在以下情况下,应用仍然可以在 Android 12 或更高版本上关闭系统对话框:
- 您的应用正在运行插桩测试。
您的应用以 Android 11 或更低版本为目标平台,并且正在显示一个位于通知抽屉顶部的窗口。
您的应用以 Android 11 或更低版本为目标平台。此外,用户已与通知互动,可能使用了通知的操作按钮,并且您的应用正在响应该用户操作处理服务或广播接收器。
您的应用以 Android 11 或更低版本为目标平台,并且具有活跃的无障碍服务。如果您的应用以 Android 12 为目标平台并希望关闭通知栏,请改用
GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE
无障碍操作。
不受信任的触摸事件被阻止
为了保持系统安全和良好的用户体验,Android 12 阻止应用在覆盖层以不安全的方式遮挡应用时消耗触摸事件。换句话说,系统会阻止穿过某些窗口的触摸,少数情况除外。
受影响的应用
此变更会影响选择让触摸穿过其窗口的应用,例如通过使用 FLAG_NOT_TOUCHABLE
标志。几个示例包括(但不限于)以下内容:
- 需要
SYSTEM_ALERT_WINDOW
权限的叠加层,例如使用TYPE_APPLICATION_OVERLAY
的窗口,并使用FLAG_NOT_TOUCHABLE
标志。 - 使用
FLAG_NOT_TOUCHABLE
标志的 Activity 窗口。
例外情况
在以下情况下,允许“穿透”触摸:
- 应用内的互动。您的应用显示叠加层,并且该叠加层仅在用户与您的应用互动时出现。
受信任的窗口。这些窗口包括(但不限于)以下内容:
完全透明的窗口。窗口的
alpha
属性为 0.0。足够半透明的系统警告窗口。当一组系统警告窗口的组合不透明度小于或等于系统对触摸的最大遮蔽不透明度时,系统认为它们是足够半透明的。在 Android 12 中,此最大不透明度默认为 0.8。
检测何时阻止不受信任的触摸
如果触摸操作被系统阻止,Logcat 会记录以下消息:
Untrusted touch due to occlusion by PACKAGE_NAME
测试变更
在运行 Android 12 或更高版本的设备上,不受信任的触摸默认会被阻止。要允许不受信任的触摸,请在终端窗口中运行以下 ADB 命令:
# A specific app adb shell am compat disable BLOCK_UNTRUSTED_TOUCHES com.example.app # All apps # If you'd still like to see a Logcat message warning when a touch would be # blocked, use 1 instead of 0. adb shell settings put global block_untrusted_touches 0
要将行为恢复为默认设置(不受信任的触摸被阻止),请运行以下命令:
# A specific app adb shell am compat reset BLOCK_UNTRUSTED_TOUCHES com.example.app # All apps adb shell settings put global block_untrusted_touches 2
Activity 生命周期
根启动器 Activity 不再在按下“返回”按钮时结束
Android 12 更改了系统在任务根目录的启动器 Activity 上处理“返回”按钮按下的默认方式。在以前的版本中,系统会在按下“返回”按钮时结束这些 Activity。在 Android 12 中,系统现在会将 Activity 及其任务移动到后台,而不是结束 Activity。新的行为与使用“主屏幕”按钮或手势退出应用时的当前行为匹配。
对于大多数应用,此变更意味着使用“返回”按钮退出应用的用户可以更快地从热启动状态恢复应用,而不必从冷启动状态完全重新启动应用。
我们建议您使用此变更测试您的应用。如果您的应用当前覆盖了 onBackPressed()
来处理“返回”导航并结束 Activity
,请更新您的实现以调用 super.onBackPressed()
而不是结束。调用 super.onBackPressed()
会在适当时候将 Activity 及其任务移动到后台,并为用户提供跨应用更一致的导航体验。
另请注意,通常,我们建议使用 AndroidX Activity API 来提供自定义返回导航,而不是覆盖 onBackPressed()
。如果没有组件拦截系统“返回”按钮按下操作,AndroidX Activity API 会自动推迟到适当的系统行为。
图形和图片
改进的刷新率切换
在 Android 12 中,使用 setFrameRate()
进行的刷新率变更可以发生,而无论显示屏是否支持到新刷新率的无缝过渡;无缝过渡是指没有任何视觉中断(例如一两秒的黑屏)的过渡。以前,如果显示屏不支持无缝过渡,则在调用 setFrameRate()
后,它通常会继续使用相同的刷新率。您可以通过调用 getAlternativeRefreshRates()
提前确定到新刷新率的过渡是否可能是无缝的。通常,回调 onDisplayChanged()
会在刷新率切换完成后调用,但对于某些外部连接的显示屏,它会在非无缝过渡期间调用。
以下是您可能如何实现此功能的示例:
Kotlin
// Determine whether the transition will be seamless. // Non-seamless transitions may cause a 1-2 second black screen. val refreshRates = this.display?.mode?.alternativeRefreshRates val willBeSeamless = Arrays.asList<FloatArray>(refreshRates).contains(newRefreshRate) // Set the frame rate even if the transition will not be seamless. surface.setFrameRate(newRefreshRate, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS)
Java
// Determine whether the transition will be seamless. // Non-seamless transitions may cause a 1-2 second black screen. Display display = context.getDisplay(); // API 30+ Display.Mode mode = display.getMode(); float[] refreshRates = mode.getAlternativeRefreshRates(); boolean willBeSeamless = Arrays.asList(refreshRates).contains(newRefreshRate); // Set the frame rate even if the transition will not be seamless. surface.setFrameRate(newRefreshRate, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS);
连接功能
Passpoint 更新
以下 API 已在 Android 12 中添加:
isPasspointTermsAndConditionsSupported()
:条款和条件是一种 Passpoint 功能,允许网络部署用安全的 Passpoint 网络替换使用开放网络的不安全强制门户。当需要接受条款和条件时,系统会向用户显示通知。建议由条款和条件限制的 Passpoint 网络的应用程序必须首先调用此 API,以确保设备支持该功能。如果设备不支持该功能,则无法连接到此网络,并且必须建议替代或旧版网络。isDecoratedIdentitySupported()
:当使用前缀修饰符对网络进行身份验证时,修饰的身份前缀允许网络运营商更新网络访问标识符 (NAI),以通过 AAA 网络内的多个代理执行显式路由(有关此内容的更多信息,请参阅RFC 7542)。Android 12 实现了此功能,以符合WBA PPS-MO 扩展规范。建议需要修饰身份的 Passpoint 网络的应用程序必须首先调用此 API,以确保设备支持该功能。如果设备不支持该功能,身份将不会被修饰,并且对网络的身份验证可能会失败。
要创建 Passpoint 建议,应用必须使用 PasspointConfiguration
、Credential
和 HomeSp
类。这些类描述了 Passpoint 配置文件,该配置文件在Wi-Fi Alliance Passpoint 规范中定义。
如需了解详情,请参阅用于互联网连接的 WLAN 建议 API。
更新了非 SDK 接口限制
Android 12 包含根据与 Android 开发者协作和最新内部测试更新的受限非 SDK 接口列表。只要有可能,我们都会确保在限制非 SDK 接口之前提供公共替代方案。
如果您的应用不以 Android 12 为目标平台,则其中一些变更可能不会立即影响您。但是,虽然您当前可以使用一些非 SDK 接口(取决于您的应用目标 API 级别),但使用任何非 SDK 方法或字段始终存在导致应用崩溃的高风险。
如果您不确定您的应用是否使用非 SDK 接口,您可以测试您的应用以找出答案。如果您的应用依赖于非 SDK 接口,您应该开始规划迁移到 SDK 替代方案。尽管如此,我们理解有些应用在使用非 SDK 接口方面有合法的用例。如果您无法找到在您的应用中为某个功能使用非 SDK 接口的替代方案,您应该请求新的公共 API。
要了解此版本 Android 中的变更,请参阅Android 12 中非 SDK 接口限制的更新。要了解有关非 SDK 接口的更多信息,请参阅非 SDK 接口限制。