行为变更:所有应用

Android 12 平台包含可能影响您应用的行为变更。以下行为变更适用于在 Android 12 上运行的所有应用,无论targetSdkVersion如何。您应测试您的应用,然后根据需要对其进行修改以适当地支持这些变更(在适用情况下)。

确保还查看仅影响以 Android 12 为目标的应用的行为变更列表

用户体验

拉伸过度滚动效果

在运行 Android 12 及更高版本的设备上,过度滚动事件的视觉行为会发生变化。

在 Android 11 及更低版本中,过度滚动事件会导致视觉元素出现辉光;在 Android 12 及更高版本中,视觉元素会在拖动事件中拉伸并回弹,在弹动事件中会弹动并回弹。

有关更多信息,请参阅有关动画滚动手势的指南。

应用启动画面

如果您之前在 Android 11 或更低版本中实现了自定义启动画面,则需要将您的应用迁移到SplashScreen API,以确保它从 Android 12 开始正确显示。如果不迁移您的应用,会导致应用启动体验下降或出现意外情况。

有关说明,请参阅将您现有的启动画面实现迁移到 Android 12

此外,从 Android 12 开始,系统始终会在所有应用的冷启动热启动时应用新的Android 系统默认启动画面。默认情况下,此系统默认启动画面是使用您应用的启动器图标元素和主题的windowBackground(如果它是单色)构建的。

有关更多详细信息,请参阅启动画面开发者指南

网络意图解析

从 Android 12(API 级别 31)开始,通用网络意图仅当您的应用已获批用于该网络意图中包含的特定域时,才会解析为应用中的活动。如果您的应用未获批用于该域,则网络意图将解析为用户的默认浏览器应用。

应用可以通过以下操作之一获取此批准

如果您的应用调用 Web 意图,请考虑添加提示或对话框以询问用户是否确认操作。

手势导航的沉浸模式改进

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 库,其中包含一个WindowMetrics 类,支持 Android 4.0(API 级别 14)及更高版本。

如何使用 WindowMetrics 的示例

首先,确保您的应用的活动是完全可调整大小的

活动应依赖于活动上下文中的WindowMetrics 进行任何与 UI 相关的操作,特别是WindowManager.getCurrentWindowMetrics() 或 Jetpack 的WindowMetricsCalculator.computeCurrentWindowMetrics()

如果您的应用创建了MediaProjection,则边界必须正确调整大小,因为投影捕获了投影仪应用正在运行的显示分区。

如果应用是完全可调整大小的,则活动上下文将返回正确的边界,如下所示

Kotlin

val projectionMetrics: WindowMetrics = activityContext
      .getSystemService(WindowManager::class.java).maximumWindowMetrics

Java

WindowMetrics projectionMetrics = activityContext
      .getSystemService(WindowManager.class).getMaximumWindowMetrics();

如果应用不可完全调整大小,则必须从WindowContext 实例中查询并使用WindowManager.getMaximumWindowMetrics() 或 Jetpack 方法WindowMetricsCalculator.computeMaximumWindowMetrics()检索活动边界的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)上,系统会检查活动的minWidthminHeight 以确定活动是否可以在多窗口模式下运行。如果resizeableActivity="false",则无论最小宽度和高度如何,都将阻止应用在多窗口模式下运行。

有关更多信息,请参阅多窗口支持

大屏幕上的相机预览

相机应用通常假设设备方向与相机预览的长宽比之间存在固定的关系。但是,大型屏幕外形规格(例如折叠设备)和多窗口和多显示器等显示模式挑战了这一假设。

在 Android 12 上,请求特定屏幕方向且不可调整大小(resizeableActivity="false")的相机应用会自动进入内嵌纵向模式,这确保了相机预览的正确方向和长宽比。在具有相机硬件抽象层(HAL)的折叠屏和其他设备上,会对相机输出应用额外的旋转以补偿相机传感器方向,并且相机输出会被裁剪以匹配应用相机预览的长宽比。裁剪和额外旋转可确保正确呈现相机预览,而不管设备方向和设备的折叠或展开状态如何。

前台服务通知的 UX 延迟

为了为短时间运行的前台服务提供简化的体验,运行 Android 12 或更高版本的设备可以将前台服务通知的显示延迟 10 秒,有一些例外情况。此更改使短暂的任务有机会在通知出现之前完成。

性能

受限应用待机桶

Android 11(API 级别 30)引入了受限桶作为应用待机桶。从 Android 12 开始,此桶默认处于活动状态。受限桶具有所有桶中最低的优先级(以及最高的限制)。按优先级从高到低的桶顺序为

  1. 活动:应用当前正在使用或最近使用。
  2. 工作集:应用处于常规使用状态。
  3. 频繁:应用经常使用,但不是每天都使用。
  4. 稀有:应用不经常使用。
  5. 受限:应用消耗大量系统资源,或可能表现出不良行为。

系统会考虑您的应用的行为以及使用模式,以决定是否将您的应用置于受限桶中。

如果您的应用更负责任地使用系统资源,则您的应用不太可能被置于受限桶中。此外,如果用户直接与您的应用交互,则系统会将您的应用置于限制较少的桶中。

检查您的应用是否在受限桶中

要检查系统是否已将您的应用置于受限桶中,请调用getAppStandbyBucket()。如果此方法的返回值为STANDBY_BUCKET_RESTRICTED,则您的应用位于受限桶中。

测试受限桶行为

要测试系统将您的应用置于受限桶中时您的应用的行为,您可以手动将您的应用移动到该桶中。为此,请在终端窗口中运行以下命令

adb shell am set-standby-bucket PACKAGE_NAME restricted

安全和隐私

近似位置

The dialog has two sets of options, one above the
         other
图 1. 允许用户授予近似位置信息的系统权限对话框。

在运行 Android 12 或更高版本的设备上,用户可以请求您的应用仅访问近似位置信息。

如果您的应用请求ACCESS_FINE_LOCATION 运行时权限,则您还应请求ACCESS_COARSE_LOCATION 权限以处理用户向您的应用授予近似位置访问权限的情况。您应在单个运行时请求中包含这两个权限。

系统权限对话框包含以下用户选项,如图 1 所示

  • 精确:提供对精确位置信息的访问权限。
  • 近似:仅提供对近似位置信息的访问权限。

麦克风和摄像头切换

支持的运行 Android 12 或更高版本的设备允许用户通过按下单个切换选项来启用和禁用设备上所有应用的摄像头和麦克风访问权限。用户可以从快速设置(如图 1 所示)或从系统设置中的“隐私”屏幕访问可切换选项。

详细了解这些切换,以及如何检查您的应用是否遵循有关CAMERARECORD_AUDIO 权限的最佳实践。

麦克风和摄像头指示器

在运行 Android 12 或更高版本的设备上,当应用访问麦克风或摄像头时,状态栏中会出现一个图标。

详细了解这些指示器,以及如何检查您的应用是否遵循有关CAMERARECORD_AUDIO 权限的最佳实践。

Quick settings tiles are labeled 'Camera access' and
         'Mic access'
图 2. 快速设置中的麦克风和摄像头切换。
A rounded rectangle in the upper-right corner, which
         includes a camera icon and a microphone icon
图 3. 麦克风和摄像头指示器,显示最近的数据访问。

权限包可见性

在运行 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 字节以外的大小初始化 Galois/Counter Mode (GCM) 密码。Conscrypt 的GcmParameterSpec 实现需要 12 字节的初始化,这是 NIST 建议的。

剪贴板访问通知

在 Android 12 及更高版本上,当应用首次调用getPrimaryClip()从其他应用访问剪贴板数据时,吐司消息会通知用户此剪贴板访问。

吐司消息内的文本包含以下格式:APP 粘贴自您的剪贴板。

剪贴板描述中的文本信息

在 Android 12 及更高版本中,getPrimaryClipDescription()可以检测以下详细信息

应用无法关闭系统对话框

为了改善用户在与应用和系统交互时的控制权,从 Android 12 开始,ACTION_CLOSE_SYSTEM_DIALOGS 意图操作已弃用。除了少数特殊情况外,当您的应用尝试调用包含此操作的意图时,系统将根据您的应用的目标 SDK 版本执行以下操作之一

  • 如果您的应用以 Android 12 或更高版本为目标,则会发生SecurityException
  • 如果您的应用以 Android 11(API 级别 30)或更低版本为目标,则意图不会执行,并且以下消息将出现在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 或更高版本上关闭系统对话框

  • 您的应用正在运行Instrumentation 测试
  • 您的应用以 Android 11 或更低版本为目标,并显示了一个位于通知抽屉顶部的窗口。

  • 您的应用以 Android 11 或更低版本为目标。此外,用户已与通知进行交互(可能使用通知的操作按钮),并且您的应用正在响应该用户操作处理服务广播接收器

  • 您的应用以 Android 11 或更低版本为目标,并且具有活动的辅助功能服务。如果您的应用以 Android 12 为目标并希望关闭通知栏,请改用GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE辅助功能操作。

阻止不受信任的触摸事件

为了维护系统安全性和良好的用户体验,Android 12 阻止应用使用触摸事件,其中叠加层以不安全的方式遮挡了应用。换句话说,系统会阻止穿过某些窗口的触摸,但也有一些例外情况

受影响的应用

此更改会影响选择允许触摸穿过其窗口的应用,例如通过使用FLAG_NOT_TOUCHABLE标志。一些示例包括但不限于以下内容

异常情况

在以下情况下,允许“直通”触摸

  • 应用内的交互。您的应用显示叠加层,并且叠加层仅在用户与您的应用交互时出现。
  • 受信任的窗口。这些窗口包括(但不限于)以下内容

  • 不可见的窗口。窗口的根视图为GONEINVISIBLE

  • 完全透明的窗口。窗口的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 更新

Android 12 中添加了以下 API

  • isPasspointTermsAndConditionsSupported()条款和条件Passpoint的一项功能,允许网络部署使用安全的 Passpoint 网络替换不安全的俘获门户(使用开放网络)。当需要接受条款和条件时,会向用户显示通知。建议使用条款和条件限制的 Passpoint 网络的应用必须首先调用此 API 以确保设备支持此功能。如果设备不支持此功能,则将无法连接到此网络,并且必须建议其他网络或旧版网络。
  • isDecoratedIdentitySupported():在使用前缀修饰进行网络身份验证时,修饰的身份前缀允许网络运营商更新网络访问标识符 (NAI) 以在 AAA 网络内执行显式路由(有关此内容的更多信息,请参阅RFC 7542)。

    Android 12 实施此功能以符合WBA PPS-MO 扩展规范。建议需要修饰身份的 Passpoint 网络的应用必须首先调用此 API 以确保设备支持此功能。如果设备不支持此功能,则身份将不会被修饰,并且网络身份验证可能会失败。

要创建 Passpoint 建议,应用必须使用PasspointConfigurationCredentialHomeSp 类。这些类描述了 Passpoint 配置文件,该文件在Wi-Fi Alliance Passpoint 规范中定义。

更多信息,请参阅用于互联网连接的 Wi-Fi 建议 API

更新的非 SDK 接口限制

Android 12 包含基于与 Android 开发人员合作和最新内部测试的更新的受限非 SDK 接口列表。只要有可能,我们都会确保在限制非 SDK 接口之前提供公共替代方案。

如果您的应用没有面向 Android 12,则其中一些更改可能不会立即影响您。但是,虽然您目前可以使用某些非 SDK 接口(取决于您的应用的目标 API 级别),但使用任何非 SDK 方法或字段始终存在使您的应用崩溃的高风险。

如果您不确定您的应用是否使用了非 SDK 接口,您可以测试您的应用以了解情况。如果您的应用依赖于非 SDK 接口,则应开始计划迁移到 SDK 替代方案。但是,我们了解某些应用有使用非 SDK 接口的有效用例。如果您找不到应用中某个功能的非 SDK 接口的替代方案,则应请求新的公共 API

要详细了解此 Android 版本中的更改,请参阅Android 12 中非 SDK 接口限制的更新。要详细了解非 SDK 接口,请参阅非 SDK 接口的限制