行为变更:面向 Android 12 的应用

与早期版本类似,Android 12 包含可能影响您应用的运行时行为变更。以下行为变更仅适用于以 Android 12 或更高版本为目标平台的应用。如果您的应用以 Android 12 为目标平台,您应在适用的情况下修改您的应用,以正确支持这些行为。

此外,务必查看影响在 Android 12 上运行的所有应用的运行时行为变更列表。

用户体验

自定义通知

Android 12 更改了完全自定义通知的外观和行为。以前,自定义通知能够使用整个通知区域并提供自己的布局和样式。这导致了一些反模式,可能会让用户感到困惑,或在不同设备上导致布局兼容性问题。

对于以 Android 12 为目标平台的应用,具有自定义内容视图的通知将不再使用整个通知区域;相反,系统会应用标准模板。此模板可确保自定义通知在所有状态下都具有与其他通知相同的修饰,例如通知的图标和展开功能(在收起状态下),以及通知的图标、应用名称和收起功能(在展开状态下)。此行为与 Notification.DecoratedCustomViewStyle 的行为几乎完全相同。

通过这种方式,Android 12 使所有通知在视觉上保持一致且易于扫描,并为用户提供了可发现、熟悉的通知展开功能。

以下插图显示了标准模板中的自定义通知

以下示例显示了自定义通知在收起状态和展开状态下的呈现方式

Android 12 中的此项更改会影响定义 Notification.Style 的自定义子类,或者使用 Notification.Builder 的方法 setCustomContentView(RemoteViews)setCustomBigContentView(RemoteViews)setCustomHeadsUpContentView(RemoteViews) 的应用。

如果您的应用使用完全自定义通知,我们建议您尽快使用新模板进行测试。

  1. 启用自定义通知更改

    1. 将应用的 targetSdkVersion 更改为 S 以启用新行为。
    2. 重新编译。
    3. 在运行 Android 12 的设备或模拟器上安装您的应用。
  2. 测试所有使用自定义视图的通知,确保它们在通知栏中的显示符合您的预期。测试时,请考虑以下因素并进行必要的调整:

    • 自定义视图的尺寸已更改。一般来说,自定义通知可用的高度比以前小。在收起状态下,自定义内容的最大高度已从 106dp 减小到 48dp。此外,水平空间也更小。

    • 对于以 Android 12 为目标平台的应用,所有通知都可展开。通常,这意味着如果您使用 setCustomContentView,您也会希望使用 setBigCustomContentView 来确保收起和展开状态一致。

    • 为确保“浮动通知”状态符合您的预期,请务必将通知渠道的重要性提高到“高”(在屏幕上弹出)。

对于以 Android 12 或更高版本为目标平台的应用,系统对 Android 应用链接的验证方式进行了一些更改。这些更改提高了应用链接体验的可靠性,并为应用开发者和最终用户提供了更多控制。

如果您依赖 Android 应用链接验证在您的应用中打开网页链接,请检查您在为 Android 应用链接验证添加 intent 过滤器时是否使用了正确的格式。特别是,请确保这些 intent 过滤器包含 BROWSABLE 类别并支持 https 方案。

您还可以手动验证您的应用链接,以测试您的声明的可靠性。

画中画行为改进

Android 12 引入了画中画 (PiP) 模式的行为改进,以及手势导航和基于元素的导航的过渡动画的推荐视觉改进。

有关更多信息,请参阅画中画改进

Toast 重新设计

在 Android 12 中,toast 视图已重新设计。Toast 现在限制为两行文本,并在文本旁边显示应用图标。

Image of Android device showing a toast popup reading
            'Sending message' next to an app icon

有关更多详细信息,请参阅Toast 概览

安全和隐私

大致位置

在运行 Android 12 或更高版本的设备上,用户可以请求您的应用获得大致位置精度

WebView 中的现代 SameSite Cookie

Android 的 WebView 组件基于 Chromium,后者是为 Google Chrome 浏览器提供支持的开源项目。Chromium 引入了对第三方 Cookie 处理方式的更改,以提供更高的安全性和隐私性,并为用户提供更高的透明度和控制力。从 Android 12 开始,当应用以 Android 12(API 级别 31)或更高版本为目标平台时,这些更改也会包含在 WebView 中。

Cookie 的 SameSite 属性控制它是否可以随任何请求发送,或者只能随同站请求发送。以下隐私保护更改改进了第三方 Cookie 的默认处理方式,并有助于防止意外的跨站点共享:

  • 没有 SameSite 属性的 Cookie 被视为 SameSite=Lax
  • 具有 SameSite=None 的 Cookie 还必须指定 Secure 属性,这意味着它们需要安全上下文,并且应通过 HTTPS 发送。
  • 站点 HTTP 和 HTTPS 版本之间的链接现在被视为跨站点请求,因此除非 Cookie 被适当地标记为 SameSite=None; Secure,否则不会发送。

对于开发者来说,一般性指导是识别您关键用户流程中的跨站点 Cookie 依赖项,并确保在需要时使用适当的值显式设置 SameSite 属性。您必须明确指定允许跨网站或跨从 HTTP 到 HTTPS 的同站导航工作的 Cookie。

有关这些更改的完整网页开发者指南,请参阅《SameSite Cookie 解释》《Schemeful SameSite》

在您的应用中测试 SameSite 行为

如果您的应用使用 WebView,或者您管理使用 Cookie 的网站或服务,我们建议您在 Android 12 WebView 上测试您的流程。如果您发现问题,您可能需要更新您的 Cookie 以支持新的 SameSite 行为。

注意登录和嵌入内容中的问题,以及用户从不安全页面开始并过渡到安全页面的登录流程、购买和身份验证流程。

要使用 WebView 测试应用,您必须通过完成以下任一步骤,为您要测试的应用启用新的 SameSite 行为:

  • 通过在WebView 开发者工具切换 UI 标志 webview-enable-modern-cookie-same-site,在测试设备上手动启用 SameSite 行为。

    这种方法允许您在运行 Android 5.0(API 级别 21)或更高版本(包括 Android 12)以及 WebView 89.0.4385.0 或更高版本的任何设备上进行测试。

  • 通过targetSdkVersion 将您的应用编译为以 Android 12(API 级别 31)为目标平台。

    如果您使用此方法,您必须使用运行 Android 12 的设备。

有关 Android 上 WebView 远程调试的信息,请参阅《Android 设备远程调试入门》

其他资源

有关 SameSite 现代行为以及 Chrome 和 WebView 推出的更多信息,请访问 Chromium SameSite 更新页面。如果您在 WebView 或 Chromium 中发现错误,可以在公共 Chromium 问题跟踪器中报告。

运动传感器已进行速率限制

为了保护用户可能敏感的信息,如果您的应用以 Android 12 或更高版本为目标平台,系统会对某些运动传感器和位置传感器的数据刷新率施加限制。

详细了解传感器速率限制

应用休眠

Android 12 扩展了 Android 11(API 级别 30)中引入的权限自动重置行为。如果您的应用以 Android 12 为目标平台,并且用户几个月没有与您的应用互动,系统将自动重置任何已授予的权限,并将您的应用置于“休眠”状态。

在有关应用休眠的指南中了解更多信息。

数据访问审核中的归因声明

Android 11(API 级别 30)中引入的数据访问审核 API 允许您根据应用的使用情况创建归因标签。这些标签使您更容易确定应用的哪个部分执行了特定类型的数据访问。

如果您的应用以 Android 12 或更高版本为目标平台,您必须在应用的清单文件中声明这些归因标签

ADB 备份限制

为了帮助保护私有应用数据,Android 12 更改了 adb backup 命令的默认行为。对于以 Android 12(API 级别 31)或更高版本为目标平台的应用,当用户运行 adb backup 命令时,应用数据将从设备导出的任何其他系统数据中排除。

如果您的测试或开发工作流依赖于使用 adb backup 的应用数据,您现在可以通过在应用的清单文件中将 android:debuggable 设置为 true 来选择导出应用数据。

更安全的组件导出

如果您的应用以 Android 12 或更高版本为目标平台,并且包含使用 intent 过滤器 activity服务广播接收器,您必须为这些应用组件显式声明 android:exported 属性。

如果应用组件包含 LAUNCHER 类别,请将 android:exported 设置为 true。在大多数其他情况下,请将 android:exported 设置为 false

以下代码片段显示了一个服务示例,该服务包含一个 intent 过滤器,其 android:exported 属性设置为 false

<service android:name="com.example.app.backgroundService"
         android:exported="false">
    <intent-filter>
        <action android:name="com.example.app.START_BACKGROUND" />
    </intent-filter>
</service>

Android Studio 中的消息

如果您的应用包含使用 intent 过滤器但未声明 android:exported 的 activity、服务或广播接收器,则根据您使用的 Android Studio 版本,会出现以下警告消息:

Android Studio 2020.3.1 Canary 11 或更高版本

显示以下消息:

  1. 您的清单文件中会出现以下 lint 警告:

    When using intent filters, please specify android:exported as well
    
  2. 当您尝试编译您的应用时,会出现以下构建错误消息:

    Manifest merger failed : Apps targeting Android 12 and higher are required \
    to specify an explicit value for android:exported when the corresponding \
    component has an intent filter defined.
    
旧版 Android Studio

如果您尝试安装应用,Logcat 会显示以下错误消息:

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'

PendingIntent 可变性

如果您的应用以 Android 12 为目标平台,您必须指定应用创建的每个 PendingIntent 对象的可变性。此额外要求可提高您应用的安全性。

测试 PendingIntent 可变性更改

要确定您的应用是否缺少可变性声明,请在 Android Studio 中查找以下 lint 警告

Warning: Missing PendingIntent mutability flag [UnspecifiedImmutableFlag]

不安全的 Intent 启动

为了提高平台安全性,Android 12 及更高版本提供了一个调试功能,可以检测 Intent 的不安全启动。当系统检测到此类不安全启动时,会发生 StrictMode 违规。

性能

前台服务启动限制

以 Android 12 或更高版本为目标平台的应用在后台运行时不能启动前台服务少数特殊情况除外。如果应用尝试在后台运行时启动前台服务,则会发生异常(少数特殊情况除外)。

考虑使用 WorkManager 在您的应用在后台运行时调度和启动加速工作。要完成用户请求的时间敏感操作,请在精确警报中启动前台服务。

精确警报权限

为了鼓励应用节约系统资源,以 Android 12 及更高版本为目标平台并设置精确警报的应用必须有权访问系统设置中“特殊应用访问”屏幕内显示的“闹钟与提醒”功能。

要获取此特殊应用访问权限,请在清单中请求 SCHEDULE_EXACT_ALARM 权限。

精确警报只能用于面向用户的功能。详细了解设置精确警报的允许用例

禁用行为变更

在您准备应用以 Android 12 为目标平台时,您可以暂时在可调试的构建变体中禁用行为变更以进行测试。为此,请完成以下任务之一:

  • 在“开发者选项”设置屏幕中,选择“应用兼容性更改”。在出现的屏幕上,点击您的应用名称,然后关闭 REQUIRE_EXACT_ALARM_PERMISSION
  • 在您的开发机器上的终端窗口中,运行以下命令:

    adb shell am compat disable REQUIRE_EXACT_ALARM_PERMISSION PACKAGE_NAME
    

通知跳转限制

当用户与通知交互时,某些应用会通过启动一个应用组件来响应通知点击,该组件最终会启动用户最终看到并与之交互的 activity。此应用组件被称为“通知跳转”。

为了提高应用性能和用户体验,以 Android 12 或更高版本为目标平台的应用无法从用作通知跳转的服务广播接收器启动 activity。换句话说,在用户点击通知或通知中的操作按钮后,您的应用不能在服务或广播接收器中调用 startActivity()

当您的应用尝试从用作通知跳转的服务或广播接收器启动 activity 时,系统会阻止该 activity 启动,并且 Logcat 中会出现以下消息:

Indirect notification activity start (trampoline) from PACKAGE_NAME, \
this should be avoided for performance reasons.

识别哪些应用组件充当通知跳转

测试您的应用时,在您点击通知后,您可以识别应用中哪个服务或广播接收器充当了通知跳转。为此,请查看以下终端命令的输出:

adb shell dumpsys activity service \
  com.android.systemui/.dump.SystemUIAuxiliaryDumpService

输出的一部分包含文本“NotifInteractionLog”。此部分包含识别由于通知点击而启动的组件所需的信息。

更新您的应用

如果您的应用从用作通知跳转的服务或广播接收器启动 activity,请完成以下迁移步骤:

  1. 创建一个与用户点击通知后看到的 activity 相关联的 PendingIntent 对象。
  2. 使用您在上一步中创建的 PendingIntent 对象作为构建通知的一部分。

为了识别 activity 的来源(例如用于日志记录),请在发布通知时使用 extras。对于集中式日志记录,请使用 ActivityLifecycleCallbacksJetpack 生命周期观察器

切换行为

测试应用的可调试版本时,您可以使用 NOTIFICATION_TRAMPOLINE_BLOCK 应用兼容性标志来启用和禁用此限制。

备份和恢复

在运行和以 Android 12(API 级别 31)为目标平台的应用中,备份和恢复的工作方式有所变化。Android 备份和恢复有两种形式:

  • 云备份:用户数据存储在用户的 Google 云端硬盘中,以便以后可以在该设备或新设备上恢复。
  • 设备到设备 (D2D) 传输:用户数据直接从其旧设备发送到用户的新设备,例如通过使用数据线。

有关如何备份和恢复数据的更多信息,请参阅《使用自动备份备份用户数据》《使用 Android 备份服务备份键值对》

D2D 传输功能更改

对于运行在 Android 12 及更高版本并以其为目标平台的应用:

  • 使用 XML 配置机制指定包含和排除规则不会影响 D2D 传输,但它仍然会影响基于云的备份和恢复(例如 Google 云端硬盘备份)。要为 D2D 传输指定规则,您必须使用下一节中介绍的新配置。

  • 在某些设备制造商的设备上,指定 android:allowBackup="false" 确实会禁用到 Google 云端硬盘的备份,但不会禁用应用的 D2D 传输。

新的包含和排除格式

运行在 Android 12 及更高版本并以其为目标平台的应用使用不同的 XML 配置格式。此格式通过要求您分别为云备份和 D2D 传输指定包含和排除规则,从而明确区分 Google 云端硬盘备份和 D2D 传输。

或者,您也可以使用它来指定备份规则,在这种情况下,在运行 Android 12 或更高版本的设备上,以前使用的配置将被忽略。对于运行 Android 11 或更低版本的设备,仍然需要旧的配置。

XML 格式变更

以下是 Android 11 及更低版本中用于备份和恢复配置的格式:

<full-backup-content>
    <include domain=["file" | "database" | "sharedpref" | "external" |
                     "root"] path="string"
    requireFlags=["clientSideEncryption" | "deviceToDeviceTransfer"] />
    <exclude domain=["file" | "database" | "sharedpref" | "external" |
                     "root"] path="string" />
</full-backup-content>

以下以粗体显示了格式中的更改。

<data-extraction-rules>
  <cloud-backup [disableIfNoEncryptionCapabilities="true|false"]>
    ...
    <include domain=["file" | "database" | "sharedpref" | "external" |
                        "root"] path="string"/>
    ...
    <exclude domain=["file" | "database" | "sharedpref" | "external" |
                        "root"] path="string"/>
    ...
  </cloud-backup>
  <device-transfer>
    ...
    <include domain=["file" | "database" | "sharedpref" | "external" |
                        "root"] path="string"/>
    ...
    <exclude domain=["file" | "database" | "sharedpref" | "external" |
                        "root"] path="string"/>
    ...
  </device-transfer>
</data-extraction-rules>

有关更多信息,请参阅《使用自动备份备份用户数据》指南中的相应部分

应用的清单标志

通过在清单文件中使用 android:dataExtractionRules 属性,将您的应用指向新的 XML 配置。当您指向新的 XML 配置时,指向旧配置的 android:fullBackupContent 属性在运行 Android 12 或更高版本的设备上将被忽略。以下代码示例显示了新的清单文件条目:

<application
    ...
    <!-- The below attribute is ignored. -->
    android:fullBackupContent="old_config.xml"
    <!-- You can point to your new configuration using the new
         dataExtractionRules attribute . -->
    android:dataExtractionRules="new_config.xml"
    ...>
</application>

连接功能

蓝牙权限

Android 12 引入了 BLUETOOTH_SCANBLUETOOTH_ADVERTISEBLUETOOTH_CONNECT 权限。这些权限使以 Android 12 为目标平台的应用更容易与蓝牙设备交互,特别是对于不需要访问设备位置的应用。

为了准备您的设备以 Android 12 或更高版本为目标平台,请更新您的应用逻辑。不要声明旧版蓝牙权限集,而是声明更现代的蓝牙权限集

并发点对点 + 互联网连接

对于以 Android 12(API 级别 31)或更高版本为目标平台的应用,支持并发点对点和互联网连接的设备可以同时保持与对等设备和主要互联网提供网络的 Wi-Fi 连接,从而使用户体验更加无缝。以 Android 11(API 级别 30)或更低版本为目标平台的应用仍然会遇到旧版行为,即在连接到对等设备之前断开主 Wi-Fi 网络。

兼容性

WifiManager.getConnectionInfo() 只能返回单个网络的 WifiInfo。因此,在 Android 12 及更高版本中,此 API 的行为已通过以下方式更改:

  • 如果只有一个 Wi-Fi 网络可用,则返回其 WifiInfo
  • 如果有多个 Wi-Fi 网络可用,并且调用应用触发了点对点连接,则返回与对等设备对应的 WifiInfo
  • 如果有多个 Wi-Fi 网络可用,并且调用应用未触发点对点连接,则返回主互联网提供连接的 WifiInfo

为了在支持双并发 Wi-Fi 网络的设备上提供更好的用户体验,我们建议所有应用(尤其是那些触发点对点连接的应用)停止调用 WifiManager.getConnectionInfo(),而是使用 NetworkCallback.onCapabilitiesChanged() 来获取与用于注册 NetworkCallbackNetworkRequest 匹配的所有 WifiInfo 对象。getConnectionInfo() 在 Android 12 中已弃用。

以下代码示例显示了如何在 NetworkCallback 中获取 WifiInfo

Kotlin

val networkCallback = object : ConnectivityManager.NetworkCallback() {
  ...
  override fun onCapabilitiesChanged(
           network : Network,
           networkCapabilities : NetworkCapabilities) {
    val transportInfo = networkCapabilities.getTransportInfo()
    if (transportInfo !is WifiInfo) return
    val wifiInfo : WifiInfo = transportInfo
    ...
  }
}

Java

final NetworkCallback networkCallback = new NetworkCallback() {
  ...
  @Override
  public void onCapabilitiesChanged(
         Network network,
         NetworkCapabilities networkCapabilities) {
    final TransportInfo transportInfo = networkCapabilities.getTransportInfo();
    if (!(transportInfo instanceof WifiInfo)) return;
    final WifiInfo wifiInfo = (WifiInfo) transportInfo;
    ...
  }
  ...
};

mDNSResponder 原生 API

Android 12 更改了应用使用 mDNSResponder 原生 API 与 mDNSResponder 守护程序交互的时间。以前,当应用在网络上注册服务并调用 getSystemService() 方法时,系统的 NSD 服务会启动 mDNSResponder 守护程序,即使应用尚未调用任何 NsdManager 方法。然后,守护程序会将设备订阅到所有节点的组播组,导致系统更频繁地唤醒并消耗额外的电量。为了最大限度地减少电池使用量,在 Android 12 及更高版本中,系统现在只在 NSD 事件需要时才启动 mDNSResponder 守护程序,并在之后停止它。

由于此更改影响 mDNSResponder 守护程序的可用时间,因此那些假设在调用 getSystemService() 方法后 mDNSResponder 守护程序将启动的应用可能会收到系统消息,提示 mDNSResponder 守护程序不可用。使用 NsdManager 且不使用 mDNSResponder 原生 API 的应用不受此更改的影响。

供应商库

供应商提供的原生共享库

如果应用以 Android 12(API 级别 31)或更高版本为目标平台,则默认情况下无法访问由芯片供应商或设备制造商提供的非 NDK 原生共享库。只有使用 <uses-native-library> 标签显式请求时,这些库才可访问。

如果应用以 Android 11(API 级别 30)或更低版本为目标平台,则不需要 <uses-native-library> 标签。在这种情况下,无论是否为 NDK 库,任何原生共享库都可访问。

更新的非 SDK 接口限制

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

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

如果您不确定您的应用是否使用非 SDK 接口,可以测试您的应用以找出答案。如果您的应用依赖于非 SDK 接口,您应该开始规划迁移到 SDK 替代方案。尽管如此,我们理解某些应用在使用非 SDK 接口方面有合理的用例。如果您无法找到使用非 SDK 接口的替代方案来实现应用中的某个功能,您应该请求新的公共 API

要了解有关此 Android 版本中更改的更多信息,请参阅《Android 12 中非 SDK 接口限制的更新》。要全面了解非 SDK 接口,请参阅《非 SDK 接口限制》