行为变更:以 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 App Links 的验证方式进行了一些更改。这些更改会提升应用链接体验的可靠性,并且可让应用开发者和最终用户更好地进行控制。

如果您依靠 Android App Links 验证在应用中打开网页链接,那么,在为 Android App Links 验证添加 intent 过滤器时,请确保使用正确的格式。尤其需要注意的是,确保这些 intent 过滤器包含 BROWSABLE 类别并支持 https 方案。

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

画中画行为改进

Android 12 改进了画中画 (PiP) 模式的行为,并建议对手势导航和基于元素的导航的过渡动画进行外观方面的改进。

如需了解详情,请参阅对画中画的改进

消息框重新设计

在 Android 12 中,消息框视图已经过重新设计。现在,消息框上限为两行文本,并且必须在文本旁边显示应用图标。

Android 设备屏幕截图,其中在应用图标旁边显示弹出式消息框“正在发送邮件”

如需了解更多详情,请参阅消息框概览

安全和隐私设置

大致位置

在搭载 Android 12 或更高版本的设备上,用户可以要求您的应用仅获取大致位置信息

WebView 中的现代 SameSite Cookie

Android 的 WebView 组件基于为 Google 的 Chrome 浏览器提供支持的开源项目 Chromium。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 不会被发送。

对于开发者而言,一般指导意见是识别关键用户流中的跨站点 Cookie 依赖项,并确保在需要时使用适当的值显式设置 SameSite 属性。您必须显式指定允许在不同网站上运行的 Cookie,或适用于从 HTTP 切换到 HTTPS 进行同站点导航的 Cookie。

如需了解有关这些变更的网站开发者完整指南,请参阅 SameSite Cookie 说明Schemeful SameSite

在您的应用中测试 SameSite 行为

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

留意登录和嵌入式内容中存在的问题,以及用户从不安全的页面启动并转到安全页面的登录流、购买和其他身份验证流。

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

  • WebView devtools切换界面标志 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 中发现了 bug,您可以在公开的 Chromium 问题跟踪器中报告该 bug。

移动传感器有采样率限制

为了保护有关用户的潜在敏感信息,如果您的应用以 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

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

<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 过滤器的 activity、服务或广播接收器但未声明 android:exported,系统会显示以下警告消息,具体取决于您使用的 Android Studio 版本:

Android Studio 2020.3.1 Canary 11 或更高版本

系统会显示以下消息:

  1. 清单文件中会显示以下 lint 警告:

    When using intent filters, please specify android:exported as well
    
  2. 当您尝试编译应用时,系统会显示以下 build 错误消息:

    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'

待处理 intent 可变性

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

测试待处理的 intent 可变性变更

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

Warning: Missing PendingIntent mutability flag [UnspecifiedImmutableFlag]

不安全的 intent 启动

为了提高平台安全性,Android 12 及更高版本提供了一种调试功能,用于检测不安全的 intent 启动。当系统检测到此类不安全的启动时,就会发生 StrictMode 违反事件。

性能

前台服务启动限制

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

当您的应用在后台运行时,请考虑使用 WorkManager 来计划和启动加急工作。如需完成用户请求的紧急操作,请按照精确的闹钟启动前台服务。

精确的闹钟权限

为了鼓励应用节省系统资源,以 Android 12 及更高版本为目标平台且设置了精确的闹钟的应用必须能够访问“闹钟和提醒”功能,该功能显示在系统设置的特殊应用访问权限屏幕中。

如需获取这种特殊应用访问权限,请在清单中请求 SCHEDULE_EXACT_ALARM 权限。

精确的闹钟只能用于面向用户的功能。详细了解设置精确闹钟的可接受用例

停用行为变更

当您准备应用以将 Android 12 作为目标平台时,您可以出于测试目的暂时停用可调试的 build 变体中的行为变更。为此,请完成以下任一任务:

  • 开发者选项设置屏幕中,选择应用兼容性变更。在随即显示的屏幕上,点按应用的名称,然后关闭 REQUIRE_EXACT_ALARM_PERMISSION
  • 在开发机器上的终端窗口中,运行以下命令:

    adb shell am compat disable REQUIRE_EXACT_ALARM_PERMISSION PACKAGE_NAME
    

通知 trampoline 限制

当用户与通知互动时,某些应用会启动一个应用组件来响应通知点按操作,该应用组件最终会启动用户最终看到并与之互动的 activity。此应用组件被称为通知 trampoline。

为了改进应用性能和用户体验,以 Android 12 或更高版本为目标平台的应用无法从用作通知 trampoline 的服务广播接收器中启动 activity。换言之,当用户点按通知或通知中的操作按钮时,您的应用无法在服务或广播接收器内调用 startActivity()

当您的应用尝试从充当通知 trampoline 的服务或广播接收器启动 activity 时,系统会阻止该 activity 启动,并在 Logcat 中显示以下消息:

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

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

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

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

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

更新应用

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

  1. 创建一个与用户点按通知后看到的 activity 关联的 PendingIntent 对象。
  2. 构建通知的过程中,使用您在上一步中创建的 PendingIntent 对象。

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

切换行为

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

备份和恢复

对于搭载 Android 12(API 级别 31)且以其为目标平台的应用,备份和恢复的工作方式发生了变更。Android 备份和恢复有两种形式:

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

如需详细了解如何备份和恢复数据,请参阅使用自动备份功能备份用户数据使用 Android Backup Service 备份键值对

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 12 或更高版本的设备上,系统会忽略指向旧配置的 android:fullBackupContent 属性。以下代码示例展示了新的清单文件条目:

<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。因此,该 API 的行为在 Android 12 及更高版本中从以下几个方面发生了变化:

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

为了在支持双并发 Wi-Fi 网络的设备上提供更好的用户体验,我们建议所有应用(特别是触发点对点连接的应用)脱离调用 WifiManager.getConnectionInfo(),而改用 NetworkCallback.onCapabilitiesChanged(),以获取与用于注册 NetworkCallbackNetworkRequest 匹配的所有 WifiInfo 对象。从 Android 12 开始,弃用了 getConnectionInfo()

以下代码示例展示了如何在 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;
    ...
  }
  ...
};

mDNSReplyer 原生 API

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

由于此变更会影响 mDNSReplyer 守护程序何时可用,因此,认为在调用 getSystemService() 方法后将启动 mDNSReplyer 守护程序的应用可能会收到来自系统的消息,指出 mDNSReplyer 守护程序不可用。使用 NsdManager 且未使用 mDNSReplyer 原生 API 的应用不受此变更的影响。

供应商库

供应商提供的原生共享库

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

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

更新后的非 SDK 限制

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

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

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

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