敬请期待 12L,这是一次将于明年年初面向大屏设备推出的功能投放。立即试用!

行为变更:所有应用

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(如果是单色)构成。

如需了解详情,请参阅启动画面开发者指南

网络 intent 解析

从 Android 12(API 级别 31)开始,仅当您的应用获准处理某个通用网络 intent 中包含的特定网域时,该网络 intent 才会解析为应用中的 activity。如果您的应用未获准处理相应的网域,则该网络 intent 会解析为用户的默认浏览器应用。

应用可通过执行以下某项操作来获准处理相应的网域:

如果您的应用调用网络 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 库,它包含一个 WindowMetrics 类,该类支持 Android 4.0(API 级别 14)及更高版本。

关于如何使用 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 的 minWidthminHeight,来确定 activity 能否在多窗口模式下运行。如果 resizeableActivity="false",则无论最小宽度和高度如何,应用都无法在多窗口模式下运行。

如需了解详情,请参阅多窗口模式支持

大屏设备上的相机预览

相机应用通常假定设备的屏幕方向和相机预览的宽高比呈固定关系。但是,大屏设备类型(例如可折叠设备)和显示模式(例如多窗口和多屏幕)挑战着这一假设。

在 Android 12 上,请求特定屏幕方向且不可调整大小 (resizeableActivity="false") 的相机应用会自动进入边衬区人像模式,从而确保相机预览的屏幕方向和宽高比正确。在可折叠设备和其他具有相机硬件抽象层 (HAL) 的设备上,会对相机输出应用额外的旋转以补偿相机传感器方向,并会剪裁相机输出以匹配应用相机预览的宽高比。无论设备屏幕方向如何以及设备是处于折叠状态还是展开状态,剪裁和额外的旋转可确保应用正确呈现相机预览。

前台服务通知的用户体验延迟

除了一些例外情况之外,为了为短时间运行的前台服务提供流畅体验,搭载 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

安全和隐私设置

麦克风和摄像头切换开关

在搭载 Android 12 或更高版本的受支持设备上,用户可以通过按一个切换开关选项,为设备上的所有应用启用和停用摄像头和麦克风使用权限。用户可以从快捷设置访问可切换的选项(如图 1 所示),也可以从系统设置中的“隐私设置”屏幕访问。

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

麦克风和摄像头指示标志

在搭载 Android 12 或更高版本的设备上,当应用使用麦克风或相机时,图标会出现在状态栏中。

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

快捷设置图块标有“摄像头使用权限”和“麦克风使用权限”
图 1. “快捷设置”中的麦克风和摄像头切换开关。
右上角的圆角矩形,其中包含相机图标和麦克风图标
图 2. 麦克风和摄像头指示标志,显示了最近的数据访问。

权限软件包可见性

在搭载 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()从另一个应用访问剪辑数据时,会弹出一个消息框消息,通知用户对剪贴板的访问。

消息框消息内的文本包含以下格式:APP pasted from your clipboard.

有关剪辑说明中文本的信息

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

应用无法关闭系统对话框

为了加强用户与应用和系统互动时的控制,从 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.
    

例外情况

在以下情况下,应用仍然可以在 Android 12 或更高版本上关闭系统对话框:

  • 您的应用运行的是插桩测试
  • 您的应用以 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() 而不是完成 Activity。调用 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 实现了此功能,以符合 PPS-MO 扩展的 WBA 规范。如果应用建议的 Passpoint 网络需要修饰的身份,应用必须先调用此 API,以确保设备支持该功能。如果设备不支持该功能,身份就不会进行修饰,并且对网络的身份验证可能会失败。

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

如需了解详情,请参阅适用于互联网连接的 Wi-Fi 建议 API

更新后的非 SDK 接口限制

Android 12 包含更新后的受限制非 SDK 接口列表(基于与 Android 开发者之间的协作以及最新的内部测试)。在限制使用非 SDK 接口之前,我们会尽可能确保有可用的公开替代方案。

如果您的应用并非以 Android 12 为目标平台,其中一些变更可能不会立即对您产生影响。然而,虽然您目前仍可以使用一些非 SDK 接口(具体取决于应用的目标 API 级别),但只要您使用任何非 SDK 方法或字段,终归存在导致应用出问题的显著风险。

如果您不确定自己的应用是否使用了非 SDK 接口,则可以测试您的应用来进行确认。如果您的应用依赖于非 SDK 接口,您应该开始计划迁移到 SDK 替代方案。然而,我们知道某些应用具有使用非 SDK 接口的有效用例。如果您无法为应用中的某项功能找到使用非 SDK 接口的替代方案,应请求新的公共 API

如需详细了解此 Android 版本中的变更,请参阅 Android 12 中有关限制非 SDK 接口的更新。如需全面了解有关非 SDK 接口的详细信息,请参阅对非 SDK 接口的限制