Android 12目前还是开发者预览版,预计到8月份会出正式版,但是官网已经给出了关于以Android 12为目标的应用适配应该注意的事项。

包含 intent 过滤器的应用组件必须声明 exported 属性

Android 12 为目标平台的应用,且包含使用 intent 过滤器activity服务广播接收器,必须为这些应用组件显式声明 android:exported

如果 activity、服务或广播接收器使用 intent 过滤器,并且未显式声明 android:exported 的值,则您的应用将无法在搭载 Android 12 的设备上进行安装。

如果您在使用 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'

如果您的应用在需要声明 android:exported 的值时未进行此声明,则 Logcat 会提供以下错误消息:

Targeting S+ (version 10000 and above) requires that an explicit value for \
android:exported be defined when intent filters are present

以下代码段显示了一个服务示例,该服务包含 intent 过滤器并针对 Android 12 进行了正确配置:

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

PendingIntent 必须声明可变性

如果以 Android 12 为目标平台,必须为应用创建的每个 PendingIntent 对象指定可变性。

要声明特定 PendingIntent 对象是否可变,请分别使用 PendingIntent.FLAG_MUTABLEPendingIntent.FLAG_IMMUTABLE

如果您的应用创建未包含设置任何可变标志的 PendingIntent 对象,系统会抛出 IllegalArgumentException,并在 Logcat 中显示以下消息:

PACKAGE_NAME: Targeting S+ (version 10000 and above) requires that one of \
FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.

Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if \
some functionality depends on the PendingIntent being mutable, e.g. if \
it needs to be used with inline replies or bubbles.

然而,某些应用需要创建可变的 PendingIntent 对象:

  • 通知中的直接回复操作需要变更与回复关联的 PendingIntent 对象中的剪辑数据。通常,您可以通过将 FILL_IN_CLIP_DATA 作为标志传递给 fillIn() 的方法请求此变更。
  • 如果您的应用使用 PendingIntent 将对话放在气泡中,则 intent 应该可变,以便系统可以应用正确的标志,例如 FLAG_ACTIVITY_MULTIPLE_TASK 和 FLAG_ACTIVITY_NEW_DOCUMENT。

如果您的应用创建了可变的 PendingIntent 对象,强烈建议您使用显式 intent 并填写 ComponentName。如此一来,每当另一个应用调用 PendingIntent 并将控制权传回您的应用时,应用中的相同组件都会启动。

以不安全的方式启动嵌套 intent

为了提高平台安全性,Android 12 提供了一种调试功能,如果您的应用以不安全的方式启动嵌套 intent,此功能便会发出警告。嵌套 intent 是在其他 intent 中作为 extra 传递的 intent

如果您的应用同时执行以下两项操作,就会发生 StrictMode 违规行为:

  • 您的应用从已传递的 intentextra 中解封嵌套 intent
  • 您的应用立即使用该嵌套 intent 启动应用组件,例如将 intent 传递给 startActivity()startService()bindService()

如何检测嵌套 intent 的不安全启动

如需检查您的应用中是否会以不安全的方式启动嵌套 intent,请在配置 VmPolicy 时调用 detectUnsafeIntentLaunch(),如以下代码段所示:

protected void onCreate() {
    StrictMode.setVmPolicy(new VmPolicy.Builder()
        // Other StrictMode checks that you've previously added.
        // ...
        .detectUnsafeIntentLaunch()
        .penaltyLog()
        // Consider also adding penaltyDeath()
        .build());
}

如果您的应用检测到 StrictMode 违规行为,您可能需要停止应用的执行以保护潜在的敏感信息。

注意:如果您的应用以 Android 12 为目标平台,并在其 VmPolicy 定义中使用 detectAll() 方法,系统将自动调用 detectUnsafeIntentLaunch() 方法。

如何最大限度地降低出现 StrictMode 违规行为的可能性

如果您的应用不可避免的会启动嵌套 intent,以便在应用的各个组件之间导航,或代表其他应用执行操作等。请执行以下操作:

  • 嵌套 intent 的内部启动确保这些组件不会被导出
  • 嵌套 intent 的跨应用启动:使用 PendingIntent 代替嵌套 intent。如此一来,当 PendingIntent 从包含它的 Intent 中解封时,应用组件可以使用调用进程的身份启动 PendingIntent。该配置允许提供程序应用向调用应用的任何组件(包括未导出的组件)发送回调。

应用再也无法在后台运行时启动前台服务

Android 12 为目标平台的应用再也无法在后台运行时启动前台服务,但一些特殊情况除外。如果应用尝试在后台运行时启动前台服务,则会引发异常(少数特殊情况除外)。当您的应用在后台运行时,请考虑使用 WorkManager

无法通过服务或广播接收器创建通知 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 的服务或广播接收器启动 activity,请完成以下迁移步骤:

1.创建一个与以下 activity 关联的 PendingIntent

  • 用户点按通知后会看到的 activity(首选)。
  • Trampoline activity 或用于启动用户在点按通知后可以看到的 activityactivity

2.在构建通知的过程中,请使用您在上一步中创建的 PendingIntent

更新限制非 SDK 接口

其实从 Android 9(API 级别 28)开始,Android 平台对应用能使用的非 SDK 接口就实施了限制。只要应用引用非 SDK 接口或尝试使用反射JNI 来获取其句柄,这些限制就适用。Android 12 更新了这个限制列表(将Android 11的部分灰名单直接加到了黑名单列表中)

区分 SDK 接口和非 SDK 接口

  • SDK 接口:在 Android 框架软件包索引中记录的那些接口
  • 非 SDK 接口:Android中私有或隐藏的 hidden api接口,这些接口方法可能在未来版本中随时可变

为最大程度地降低非 SDK 使用限制对开发工作流的影响,我们将非 SDK 接口分成了几个名单,这些名单界定了非 SDK 接口使用限制的严格程度(取决于应用的目标 API 级别)

名单

说明

黑名单 (blacklist)

无论应用的目标 API 级别是什么,您都无法使用的非 SDK 接口。 如果您的应用尝试访问其中任何一个接口,系统就会抛出错误。

有条件灰名单 (greylist-max-x)

从 Android 9(API 级别 28)开始,当有应用以该 API 级别为目标平台时,我们会在每个 API 级别分别限制某些非 SDK 接口。

这些名单会以应用无法再访问该名单中的非 SDK 接口之前可以作为目标平台的最高 API 级别 (max-target-x) 进行标记。例如,在 Android Pie 中未被屏蔽、但现在已被 Android 10 屏蔽的非 SDK 接口会列入 max-target-p (greylist-max-p) 名单,其中的“p”表示 Pie 或 Android 9(API 级别 28)。

如果您的应用尝试访问受目标 API 级别限制的接口,系统就会将此 API 视为已列入屏蔽名单。

灰名单 (greylist)

当前不受限制且您的应用可以使用的非 SDK 接口。 但请注意,这些接口不受支持,可能会在未发出通知的情况下随时发生更改。预计这些接口在未来的 Android 版本中会被有条件地屏蔽,并列在 max-target-x 名单中。

白名单 (whitelist)

已在 Android 框架软件包索引中正式记录、受支持并且可以自由使用的接口。

如何确定我当前使用的接口属于哪个名单?

  1. 官方给出了文件下载地址,可以到这里下载:确定接口属于哪个名单
  2. 可以参考这里:Android 12 中有关限制非 SDK 接口的更新
  3. 可以在FrameWork源码中搜索hiddenapi关键字,搜索结果中的hiddenapi-force-blacklist.txthiddenapi-light-greylist.txt 就是黑名单和灰名单列表,推荐 Android Source 这个网站可在线搜索

如果用反射或者JNI调用了这些非SDK接口会导致什么影响:

访问方式

结果

Dalvik 指令引用某个字段

抛出 NoSuchFieldError

Dalvik 指令引用某个方法

抛出 NoSuchMethodError

通过 Class.getDeclaredField() 或 Class.getField() 进行反射

抛出 NoSuchFieldException

通过 Class.getDeclaredMethod()、Class.getMethod() 进行反射

抛出 NoSuchMethodException

通过 Class.getDeclaredFields()、Class.getFields() 进行反射

结果中未获取到非 SDK 成员

通过 Class.getDeclaredMethods()、Class.getMethods() 进行反射

结果中未获取到非 SDK 成员

通过 env->GetFieldID() 进行 JNI 调用

返回 NULL,抛出 NoSuchFieldError

通过 env->GetMethodID() 进行 JNI 调用

返回 NULL,抛出 NoSuchMethodError

统一规范了自定义通知栏的显示区域

Android 12 改变了完全自定义通知的外观。 以前,自定义通知能够使用整个通知区域并提供自己的布局和样式。由此产生的反模式可能会令用户困惑,或在不同设备上引发布局兼容性问题。

对于以 Android 12 为目标平台的应用,包含自定义内容视图的通知将不再使用完整通知区域;相反,系统会应用标准模板。此模板可确保自定义通知在所有状态下都与其他通知相同。

换言之,应用不能在整个通知栏区域进行个性化了,大家都长一个样。这是为了保持通知外观一致且易于浏览。

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

下载android版本下载安装 android12下载_Android 12行为变更


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

下载android版本下载安装 android12下载_Android S_02


下载android版本下载安装 android12下载_Android S_03

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

如果您的应用使用的是完全自定义的通知,建议尽快使用新模板进行测试,并进行必要的调整:

  1. 启用自定义通知变更:
    a. 将应用的 targetSdkVersion 变更为 S 以启用新行为。
    b. 重新编译。
    c. 在搭载 Android 12 的设备或模拟器上安装您的应用。
  2. 测试所有使用自定义视图的通知,确保这些通知在通知栏中看起来符合预期。
  3. 请注意自定义视图规格。一般来说,提供给自定义通知的高度比之前小。在收起状态下,自定义内容的最大高度已从 106dp 减少到 48dp。此外,水平空间也减小了。
  4. 为了确保“浮动通知”状态看起来符合您的预期,请勿忘记将通知渠道的重要性提升至“高”(在屏幕中弹出)。

SameSite Cookie 行为将被应用于 WebView

什么是 SameSite Cookie

可参考以下文章了解:

CookieSameSite 属性决定了它是可以与任何请求一起发送,还是只能与同站点请求一起发送。Android 12 中的 WebView 基础版本包含以下隐私保护方面的变更,旨在改善对第三方 Cookie 的默认处理方式,并帮助防止意外跨站点共享:

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

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

adb 备份限制

为了保护私有应用数据,Android 12 变更了 adb backup 命令的默认行为。对于以 Android 12 为目标平台的应用,用户运行 adb backup 命令时,从设备导出的任何其他系统数据都不包含应用数据

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

以上,可参考官网:行为变更:以 Android 12 为目标平台的应用