Android 12 行为变更:适配以Android 12为目标的应用

2022年7月11日08:15:22

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+(version10000 and above) requires that an explicit valuefor \
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+(version10000 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_MUTABLEif \
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(),如以下代码段所示:

protectedvoidonCreate(){
    StrictMode.setVmPolicy(newVmPolicy.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 avoidedfor 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 12 行为变更:适配以Android 12为目标的应用
以下示例展示了在收起状态和展开状态下呈现的自定义通知:
Android 12 行为变更:适配以Android 12为目标的应用
Android 12 行为变更:适配以Android 12为目标的应用

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 为目标平台的应用

  • 作者:川峰
  • 原文链接:https://blog.csdn.net/lyabc123456/article/details/117232115
    更新时间:2022年7月11日08:15:22 ,共 6558 字。