Android 12 应用适配指南3 影响应用的行为变更

Android 12 平台包含一些行为变更,这些变更可能会影响您的应用。以下行为变更将影响在 Android 12 上运行的所有应用,无论采用哪种 targetSdkVersion都不例外

Android 12 应用适配指南3 影响应用的行为变更

影响应用的行为变更

4.1 行为变更:所有应用

Android 12 平台包含一些行为变更,这些变更可能会影响您的应用。以下行为变更将影响在 Android 12 上运行的所有应用,无论采用哪种 targetSdkVersion都不例外。您应该充分测试您的应用,相应做出修改以支持这些变更。

4.1.1 沉浸模式下的手势导航改进

Android 12 简化了沉浸模式,使手势导航更易于操作且与其他活动体验(例如观看视频和阅读图书)更加一致。应用仍然可以在全屏游戏体验中防止意外手势,以免用户在玩游戏时意外退出游戏;所有其他全屏或沉浸式体验现在都允许用户通过滑动手势进行导航。为了实现这一点,从 Android 12 开始,已弃用非粘性沉浸式体验(​BEHAVIOR_SHOW_BARS_BY_TOUCH​、​BEHAVIOR_SHOW_BARS_BY_SWIPE​)的现有行为。它们已被默认行为 (​BEHAVIOR_DEFAULT​) 取代,即在隐藏系统栏后,允许使用滑动手势。此标志会根据模式显示不同的视觉和功能行为:

  • 在“三按钮”模式下,视觉和功能行为与 Android 12 之前的版本中的沉浸模式相同。
  • 在手势导航模式下,行为如下:
  • 在视觉上,它与 Android 11 及更低版本中的沉浸模式相同。从功能上讲,即使系统栏被隐藏,也允许使用手势;只需滑动屏幕一次便可调用系统返回操作,而 Android 11 需要滑动两次。下拉通知栏或转到主屏幕无需另外滑动屏幕。

Android 12 的粘性沉浸模式 (​BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE​) 没有任何变化。请注意此功能的以下向后兼容性:

  • 如果应用以 Android 11 及更低版本为目标平台,则在 Android 12 上运行时:
  • ​BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE​ 展示出的功能和呈现出的视觉效果相同。默认值会映射到 ​BEHAVIOR_SHOW_BARS_BY_SWIPE​。
  • 如果应用以 Android 12 为目标平台,则在 Android 11(API 级别 30)及更低版本上运行时:
  • 展示出的功能和呈现出的视觉效果应该是相同的,但 ​BEHAVIOR_SHOW_BARS_BY_TOUCH​ 映射到 ​BEHAVIOR_SHOW_BARS_BY_SWIPE​ 的情况除外。请务必更新您的 SDK 级别,以获得新的默认值 (​BEHAVIOR_SHOW_BARS_BY_SWIPE​)。否则,​BEHAVIOR_SHOW_BARS_BY_TOUCH​ 的默认值会保持不变。

4.1.2 前台服务通知延迟

在Android 12上为了优化短时间运行的前台服务的流畅体验,系统会延迟10s显示该前台服务的通知。此更改使某些短期任务在显示通知前完成。如果某项前台服务至少具有以下特征之一,则系统会在服务启动后立即显示相关通知:

  1. 该服务与包含“操作按钮”的通知相关联。
  2. 该服务的foregroundServiceType为 ​connectedDevice​、​mediaPlayback​、​mediaProjection​ 或 ​phoneCall​。
  3. 该服务根据通知的category中的定义,提供与通话、导航或媒体播放相关的用例。
  4. 该服务通过在设置通知时调用setShowForegroundImmediately()

4.1.3 对 Netlink MAC 地址的限制

Android 12 进一步限制了所有非系统应用对设备的 MAC 地址(不可重置的标识符)的访问,应用无论采用哪种targetSdkVersion都不例外。相关 API 会返回空值或占位值,具体取决于应用的targetSdkVersion:

  • 如果应用以 Android 12 为目标平台,则该 API 会返回 null。
  • 如果应用以 Android 11 或更低版本为目标平台,则该 API 将返回硬编码占位值:​02:00:00:00:00:00​

如果您的应用访问了MAC地址,logcat 输出会显示:CompatibilityChangeReporter: Compat change id reported: 170188668

4.1.4 不受信任的触摸事件被屏蔽

为了维持系统安全并保持良好的用户体验,Android 12 会阻止应用使用触摸事件,使用触摸事件时叠加层会以不安全的方式遮掩应用。 换言之,系统会屏蔽穿透某些窗口的触摸操作,但存在一些例外情况。

4.1.4.1 受影响的应用

此变更会影响选择让触摸操作穿透其窗口的应用,例如使用 ​FLAG_NOT_TOUCHABLE​ 标志。包括但不限于以下示例:

  • 需要 ​SYSTEM_ALERT_WINDOW​ 权限并使用 ​FLAG_NOT_TOUCHABLE​ 标志的叠加层,例如使用 ​TYPE_APPLICATION_OVERLAY​ 的窗口。
  • 使用 ​FLAG_NOT_TOUCHABLE​ 标志的 activity 窗口。
  • Toast窗口

例如,使用以上示例创建的窗口,在Android 12上会呈现出不可“穿透”的触摸操作:

Android 12 应用适配指南3 影响应用的行为变更

如示例图所示,触摸操作将无法穿透窗口,即无法点击窗口内的应用图标。

4.1.4.2 适配方法

在以下情况下,允许执行“穿透”触摸操作:

注意:类型为TYPE_APPLICATION_OVERLAY的窗口不受信任。

  • 不可见窗口。窗口的根视图是 GONE 或 INVISIBLE。
  • 全透明窗口。窗口的 alpha 属性为 0.0。
  • 足够半透明的系统警报窗口。当组合后的不透明度小于或等于系统针对触摸的最大遮掩不透明度时,系统会将一组系统警报窗口视为足够半透明。在 Android 12 Beta 版中,这一最大不透明度为 0.8。此值在未来的 Beta 版中可能会发生变化,可通过如下方式获取:
InputManager inputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
Log.d("最大允许的透明度:",""+inputManager.getMaximumObscuringOpacityForTouch());
4.1.4.3 检测不受信任的触摸操作是否被屏蔽

如果系统屏蔽触摸操作,Logcat 会记录以下消息:

Untrusted touch due to occlusion by package_name
4.1.4.4 测试变更

运行 Android 12 开发者预览版3的设备默认已屏蔽不受信任的触摸操作。如需允许不受信任的触摸操作,请在终端窗口中运行以下命令:

# 单个应用
adb shell am compat disable BLOCK_UNTRUSTED_TOUCHES com.example.app

# 对所有应用生效
# 设置成0,表示允许,设置成1,日志会打印对应的信息
adb shell settings put global block_untrusted_touches 0

如需将行为还原为默认设置(不受信任的触摸操作被屏蔽),请运行以下命令:

# 单个应用
adb shell am compat reset BLOCK_UNTRUSTED_TOUCHES com.example.app

# 对所有应用生效
adb shell settings put global block_untrusted_touches 2

4.1.5 应用无法关闭系统对话框

为了加强用户与应用和系统互动时的控制,从 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 或更低版本为目标平台,并在抽屉式通知栏顶部显示一个窗口。

4.1.6 Display#getRealSize 和 getRealMetrics:废弃和沙盒

Android 设备有许多不同的外形规格,如大屏设备、平板电脑和可折叠设备。为了针对每种设备适当地呈现内容,您的应用需要确定屏幕或显示屏尺寸。随着时间的推移,Android 提供了不同的 API 来检索此信息。在 Android 11 中,我们引入了WindowMetricsAPI 并废弃了以下方法:

在 Android 12 中,我们继续建议使用 WindowMetrics,并且正在逐步废弃以下方法:

为了缓解应用使用 Display API 检索应用边界的行为,Android 12 添加了一种新的沙盒机制来更正这些 API 返回的信息。这可能会对将此信息与 MediaProjection 一起使用的应用产生影响。

应用应使用 WindowMetrics API 查询其窗口的边界,并使用Configuration.densityDpi查询当前的密度。

为了与较低的 Android 版本实现更广泛的兼容性,您可以使用 JetpackWindowManager库,它包含一个WindowMetrics 类,该类支持 Android 4.0(API 级别 14)及更高版本。

关于如何使用 WindowMetrics 的示例

首先,确保应用的 activity完全可调整大小

activity 应依赖于来自 activity 上下文的 WindowMetrics 来执行任何与界面相关的工作,特别是WindowManager.getCurrentWindowMetrics()

如果您的应用创建了 MediaProjection,则必须正确地调整边界的大小,因为投影会捕捉显示内容。如果应用完全可调整大小,则 activity 上下文会返回正确的边界,如下所示:

WindowMetrics projectionMetrics = activityContext
        .getSystemService(WindowManager.class).getMaximumWindowMetrics();

如果应用并非完全可调整大小,则必须从 WindowContext 实例查询边界,并使用WindowManager.getMaximumWindowMetrics()检索应用可用的最大显示区域的 WindowMetrics。

Context windowContext = mContext.createWindowContext(mContext.getDisplay(),
        TYPE_APPLICATION, null /* options */);
WindowMetrics projectionMetrics = windowContext.getWindowManager()
        .getMaximumWindowMetrics();

注意:使用 MediaProjection 的任何库也应遵循此建议,并查询应用窗口的相应 WindowMetrics。

4.1.7 改进了刷新率切换

在 Android 12 中,无论显示屏是否支持无缝过渡到新的刷新率,都会发生使用setFrameRate()实现的刷新率变化;无缝过渡是指没有任何视觉中断,比如一两秒钟的黑屏。以前,如果显示屏不支持无缝过渡,它在调用 setFrameRate() 后通常会继续使用同一刷新率。您可以调用getAlternativeRefreshRates()来提前确定向新刷新率的过渡是否有可能是无缝过渡。通常,会在刷新率切换完成后调用回调onDisplayChanged(),但对于某些外接显示屏,会在非无缝过渡期间调用该回调。

以下示例说明了您可以如何实现此行为:

// 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);

4.1.8 麦克风和摄像头切换开关

在 Android 12 中,受支持的设备允许用户通过按一个切换开关选项,为设备上的所有应用启用和停用摄像头和麦克风使用权限。用户可以从快捷设置访问可切换的选项,也可以从系统设置中的“隐私设置”屏幕访问。

摄像头和麦克风切换开关会影响设备上的所有应用:

注意:当用户拨打应急服务电话(如 911)时,系统会开启麦克风使用权限。此行为可保护用户安全。

当用户关闭摄像头或麦克风的使用权限,然后启动需要使用摄像头或麦克风信息的应用时,系统会提醒用户,设备范围的切换开关已关闭。

检查给定的设备是否支持麦克风和摄像头切换开关

如需检查设备是否支持麦克风和摄像头切换开关,请添加以下代码段中所示的逻辑:

SensorPrivacyManager sensorPrivacyManager = getApplicationContext() .getSystemService(SensorPrivacyManager.class); boolean supportsMicrophoneToggle = sensorPrivacyManager .supportsSensorToggle(Sensors.MICROPHONE); boolean supportsCameraToggle = sensorPrivacyManager .supportsSensorToggle(Sensors.CAMERA);

检查响应麦克风和摄像头切换开关的应用行为

麦克风和摄像头切换开关不应影响您的应用处理CAMERARECORD_AUDIO权限的方式,前提是您遵循关于 Android 权限的最佳做法

特别是,确保您的应用做到以下几点:

  • 等到用户向您的应用授予 CAMERA 权限后再使用设备的摄像头。
  • 等到用户向您的应用授予 RECORD_AUDIO 权限后再使用设备的麦克风。

4.1.9 麦克风和摄像头指示标志

在搭载 Android 12 的设备上,当应用使用麦克风或摄像头时,图标会出现在状态栏中。如果应用处于沉浸模式,则图标会出现在屏幕的右上角。用户可以打开“快捷设置”,并选择图标以查看哪些应用当前正在使用麦克风或摄像头。

为了提供更好的用户体验,在用户明确向您的应用授予权限之前,请勿使用麦克风或摄像头。

4.1.10 权限软件包可见性

在搭载 Android 12 的设备上,根据应用对其他应用的软件包可见性,以 Android 11(API 级别 30)或更高版本为目标平台且调用以下某种方法的应用会收到一组过滤后的结果:

4.1.11 移除了 Bouncy Castle 实现

Android 12 移除了之前废弃的加密算法(包括所有 AES 算法)的许多BouncyCastle实现。系统改用这些算法的Conscrypt实现。

如果符合以下任何条件,则此变更会影响您的应用:

  • 您的应用使用 512 位的密钥大小。Conscrypt 不支持此密钥大小。如有必要,请更新您应用的加密逻辑以使用其他密钥大小。
  • 您的应用将无效的密钥大小与 KeyGenerator 一起使用。与 BouncyCastle 相比,Conscrypt 的 KeyGenerator 实现会对密钥参数执行额外的验证。例如,Conscrypt 不允许您的应用生成 64 位 AES 密钥,因为 AES 仅支持 128 位、192 位和 256 位密钥。

BouncyCastle 允许生成大小无效的密钥,但如果稍后这些密钥与Cipher一起使用,验证会失败。如果使用 Conscrypt,验证失败的时间会更早。

  • 您使用并非 12 字节的大小初始化伽罗瓦/计数器模式 (GCM) 加密。Conscrypt 的 GcmParameterSpec 实现要求初始化为 12 字节,这是 NIST 推荐的做法。

4.1.12 剪贴板访问通知

在 Android 12 中,当某个应用首次调用getPrimaryClip()以访问来自其他应用的 ClipData 时,系统会显示一条消息框消息,通知用户此次剪贴板访问。

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

检索剪贴说明时未显示消息

您的应用可能会调用getPrimaryClipDescription()以接收有关剪贴板上当前数据的信息。当您的应用调用此方法时,系统不会显示消息框消息。

Android 12 增强了此方法以检测下面这些额外的详细信息:

4.1.13 应用两个 Task 之间的过渡动画

Android 12 版本在两个 Task 之间的过渡动画逻辑发生了变更:

1.使用 Activity.overridePendingAppTransition 接口提供给系统的过渡动画无法作用于 Task 之间跳转。

2.使用 ActivityOptions.makeCustomAnimation 接口创建的过渡动画无法作用于 Task 之间跳转。

如果您的应用运行在 Android 12 设备上,且出现两个 Task 跳转时无过渡动画的情况,建议参考以下方案恢复 Task 动画的正常播放:

第一步,在项目资源目录下为 Task 创建过渡动画资源文件,如:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:fromXScale="1"
        android:toXScale="0.5"
        android:fromYScale="1"
        android:toYScale="0.5"
        android:pivotX="50%"
        android:pivotY="50%"
        android:fillAfter="false"
        android:duration="200" />

</set>

第二步,在项目主题资源文件中配置 Task 过渡动画,如:

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.MyApplication" parent="Theme.AppCompat">
        //在主题中引用自定义的过渡动画
        <item name="android:windowAnimationStyle">@style/ActivitAnimStyle</item>
        //禁止启动动画,防止启动动画影响自定义动画的播放
        <item name="android:windowDisablePreview">true</item>
    </style>


    <style name="ActivitAnimStyle" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <item name="android:taskOpenEnterAnimation">@anim/anim_xxx</item>
        <item name="android:taskOpenExitAnimation">@anim/anim_xxx</item>
        <item name="android:taskCloseEnterAnimation">@anim/anim_xxx</item>
        <item name="android:taskCloseExitAnimation">@anim/anim_xxx</item>
    </style>
</resources>

第三步,在 AndroidManifest.xml 文件中应用自定义主题,如:

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true">
	<activity
        android:name=".MainActivity"
        android:exported="true"
        android:theme="@style/Theme.MyApplication">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <activity android:name=".Secondctivity"
        android:launchMode="singleTask"
        android:taskAffinity=".NewTask"
        android:theme="@style/Theme.MyApplication"
        android:exported="true">
    </activity>
</application>

4.2 行为变更:以Android 12为目标平台的应用

4.2.1 WebView中SameSite cookie行为

Android 的 WebView 组件基于为 Google 开源项目 Chromium。在过去一年中,Chromium 变更了对第三方 Cookie 的处理方式,目的是为了更好地保护用户的安全和隐私,并赋予用户更高的透明度和控制权。从 Android 12 开始,这些变更将应用于 WebView 中。

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

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

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

如需了解有关这些变更的更详细内容,请参阅:WebView 中的现代 SameSite Cookie 行为

4.2.2 adb备份

为了保护私有应用数据,Android 12 变更了 ​adb backup​ 命令的默认行为。对于以 Android 12 为目标平台的应用,用户运行 ​adb backup​ 命令时,从设备导出的任何其他系统数据都不包含应用数据。如果您的测试或开发工作流程依赖于使用 ​adb backup​ 的应用数据,现在您可以选择通过在应用的清单文件中将 ​android:debuggable​ 设置为 ​true​ 来导出应用数据。注意:为了帮助保护应用数据,请务必在发布应用前将 ​android:debuggable​ 设置为 ​false​。

4.2.3 组件安全性

4.2.3.1 更安全的exported组件

如果您的应用以 Android 12 为目标平台,且包含使用<intent-filter>的 activity、服务或广播接收器,您必须为这些应用组件显式声明 ​android:exported​ 属性。注意:targetSdkVersion为Android 12的应用,如果activity、服务或广播接收器使用 intent 过滤器,并且未显式声明 ​android:exported​ 的值,则您的应用将无法在搭载 Android 12 的设备上进行安装。强制安装时会有提示:

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

以下为正确的服务配置代码段:

<service android:name="com.example.app.xxxService"
         android:exported="false">
    <intent-filter>
        <action android:name="com.example.app.xxxxAction" />
    </intent-filter>
</service>
4.2.3.2 PendingIntent必须声明可变性

如果您的应用以 Android 12 为目标平台,您必须为您的应用创建的每个 ​PendingIntent​ 对象指定可变性。这项额外的要求可提高应用的安全性。如需声明特定 ​PendingIntent​ 对象是否可变,请分别使用 ​PendingIntent.FLAG_MUTABLE​ 或 ​PendingIntent.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​ 对象不可变,则应用无法修改 intent 来调整调用 intent 的结果。

PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(),
        requestCode, intent,/*flags */ PendingIntent.FLAG_IMMUTABLE);

有些情况下,应用需要创建可变的​PendingIntent​ 对象 :

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

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

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

嵌套 intent 是在其他 intent 中作为 extra 传递的 intent。为了提高平台安全性,Android 12 提供了一种调试功能,如果您的应用以不安全的方式启动嵌套 intent,此功能便会发出警告。如果您的应用同时执行以下两项操作,就会发生 StrictMode 违规行为。

  1. 您的应用从已传递的 intent 的 extra 中解封嵌套 intent。
  2. 您的应用立即使用该嵌套 intent 启动应用组件,例如将 intent 传递给 ​startActivity()​、​startService()​ 或 ​bindService()​。

如需检查您的应用中是否会以不安全的方式启动嵌套 intent,请在配置 ​VmPolicy​ 时调用 ​detectUnsafeIntentLaunch()​,如以下代码段所示。如果您的应用检测到 StrictMode 违规行为,您可能需要停止应用的执行以保护潜在的敏感信息。

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

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

您的应用可能会启动嵌套 intent,以便在应用的各个组件之间导航,或代表其他应用执行操作。如需在这两种情况下最大限度地降低出现 StrictMode 违规行为的可能性,请执行以下操作:

  • 嵌套 intent 的内部启动:组件配置为 android:exported=”false” 。
  • 嵌套 intent 的跨应用启动:使用 ​PendingIntent​ 代替嵌套 intent。这样当 ​PendingIntent​ 从包含它的 ​Intent​ 中解封时,应用组件可以使用调用进程的身份启动 ​PendingIntent​。该配置允许提供程序应用向调用应用的任何组件(包括未导出的组件)发送回调。

4.2.4 前台服务启动限制

以Android 12为目标平台的应用在后台运行时无法再启动前台服务,但一些特殊情况除外。如果应用在后台运行时尝试启动前台服务且不符合特殊情况的,系统会抛出IllegalStateException。允许从后台启动前台服务的情况,请参考developer文档。

系统推荐替代前台服务的方案是WorkManager,Android 12 Beta 版发布时,WorkManager 将成为启动高优先级后台任务的推荐解决方案。Work Manager 2.7.0-alpha01 支持加急作业,后者是 Android 12 中的一种新作业。在 Android 11 及更低版本中,此版本的 WorkManager 使用前台服务提供向后兼容性。关于加急作业和WorkManager的更多介绍,请参考:加急作业

4.2.5 非SDK接口限制

从Android 9(API级别28)开始,Android平台对应用使用非SDK接口实施了限制。这些限制旨在提升用户体验,降低应用发生崩溃的风险,同时也为开发者降低紧急发布的风险。当应用通过引用非 SDK 接口或尝试使用反射或 JNI 来获取其句柄,这些限制就会生效,常见的访问非SDK接口,系统会抛出NoSuchFieldError,NoSuchMethodError,NoSuchFieldException,NoSuchMethodException,返回的结果可能是NULL。在Android 12中包含了更新后的受限制非SDK接口列表,在限制之前,Android会尽可能确保有可用的替代方案。如果您的应用依赖于非 SDK 接口,您应该开始计划迁移到 SDK 替代方案。如果您无法为应用中的某项功能找到使用非 SDK 接口的替代方案,应请求新的公共 API

Android 12 中的列表变更分为以下几类:

  • Android 11(API 级别 30)中不受支持的(已被列入灰名单)的非 SDK 接口现已在 Android 12 中被禁止使用
  • Android 11(API 级别 30)中受限的非 SDK 接口,它们在 Android 12 被添加到了 Android SDK 中。

如需查看 Android 12 的所有非 SDK 接口的完整列表,请下载以下文件:​hiddenapi-flags.csv​关于更多限制非SDK接口访问的内容,请参考:针对非 SDK 接口的限制

4.2.6 自定义通知变更

Android 12 改变了完全自定义通知的外观。 以前,自定义通知能够使用整个通知区域并提供自己的布局和样式,这可能在不同设备上引发布局兼容性问题。
对于以 Android 12 为目标平台的应用,包含自定义内容视图的通知将不再使用完整通知区域;相反,系统会应用标准模板。此模板可确保自定义通知在所有状态下都与其他通知相同,例如,在收起状态下的通知图标和展开功能,以及在展开状态下的通知图标、应用名称和收起功能。此行为与 ​Notification.DecoratedCustomViewStyle​ 的行为几乎完全相同。
通过这种方式,Android 12 通过为用户提供可看到且熟悉的通知展开功能,使所有通知保持外观一致且易于浏览。下图显示了标准模板中的自定义通知:

Android 12 应用适配指南3 影响应用的行为变更

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

Android 12 应用适配指南3 影响应用的行为变更
Android 12 应用适配指南3 影响应用的行为变更

Android 12 中的变更会影响某些定义 ​Notification.Style​ 的自定义子类的应用,或使用 ​Notification.Builder​ 的方法 ​setCustomContentView(RemoteViews)​、​setCustomBigContentView(RemoteViews)​ 和 ​setCustomHeadsUpContentView(RemoteViews)​ 的应用。如果您的应用使用的是完全自定义的通知,我们建议您尽快使用新模板进行测试,并进行必要的调整:

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

4.2.7 连接性

以 Android 12 及更高版本为目标平台的设备在具有硬件支持的设备上运行时,在与对等设备建立连接时,使用点对点连接不会断开与现有 Wi-Fi 的连接。如需检查是否支持此功能,请使用 ​WifiManager.isMultiStaConcurrencySupported()​。

4.2.8 大致位置

使用以 Android 12 为目标平台的应用时,用户可以请求应用只能访问大致位置信息。

注意:如果您的应用请求 ACCESS_COARSE_LOCATION 但未请求 ACCESS_FINE_LOCATION,则此变更不会影响您的应用。

如果您的应用以 Android 12 为目标平台并且请求ACCESS_FINE_LOCATION运行时权限,则您还必须请求ACCESS_COARSE_LOCATION权限。您必须在单个运行时请求中包含这两项权限。

当您的应用同时请求 ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION 时,系统权限对话框将为用户提供以下新选项,如图 1 所示:

  • 确切:提供 ACCESS_FINE_LOCATION 权限提供的位置精确度。
  • 大致:提供 ACCESS_COARSE_LOCATION 权限提供的位置精确度。

详细了解 Android 12 中的大致位置

4.2.9 应用休眠

Android 12 在 Android 11(API 级别 30)中引入的自动重置权限行为的基础上进行了扩展。如果您的应用以 Android 12 为目标平台并且用户有几个月未与您的应用互动,则系统会自动重置授予的所有权限并将您的应用置于休眠状态。

注意:如果您的应用是实现DeviceAdminService或者在设备所有者或个人资料所有者操作模式下运行的设备政策控制器,则系统绝不会将您的应用置于休眠状态。

处于休眠状态的应用具有以下特征:

  • 系统会针对存储空间而非性能进行优化。应用缓存中的任何文件都会被移除。
  • 应用无法从后台运行作业或提醒。
  • 应用无法接收推送通知,包括通过 Firebase Cloud Messaging 发送的高优先级消息。

当用户下次与应用互动时,应用会退出休眠状态,并且可以再次创建作业、提醒和通知。不过,您需要重新调度在应用进入休眠状态之前调度的所有作业、提醒和通知。此工作流与用户从系统设置中手动强行停止应用时的工作流类似。为了更轻松地支持此工作流,请使用WorkManager。您还可以在ACTION_BOOT_COMPLETED 广播接收器中添加重新调度逻辑,系统会在您的应用离开休眠状态时以及设备启动后调用它。

请求用户停用休眠

如果您预计应用中的某个用例会受休眠的影响,您可以向用户发送请求,让其准许应用免于休眠和自动重置权限。如果用户希望应用主要在后台运行,即使用户不与应用互动,应用也能正常工作,例如,当应用执行以下一项或多项操作时,这种豁免很有用:

  • 通过定期报告家庭成员的位置来保障家庭安全。
  • 在设备与应用的服务器之间同步数据。
  • 与智能设备(如电视)通信。
  • 与配套设备(如手表)配对。

如需请求豁免,请调用包含Intent.ACTION_APPLICATION_DETAILS_SETTINGSintent 操作的 intent。在显示的屏幕中,用户可以关闭名为撤消权限并释放空间的选项。

注意:在调用 intent 之前,不妨考虑向用户显示一个指导界面,以便用户了解为什么您的应用将他们引导至系统设置。

测试休眠行为

如需将应用置于休眠状态以进行测试,请执行以下操作:

1.在设备上启用该行为:

adb shell device_config put app_hibernation app_hibernation_enabled true

2.更改应用的状态,使其进入休眠状态。包含 –global 标志的命令会强制应用进入“完全休眠”状态,以模拟系统已为多用户设备上的所有用户将应用置于休眠状态的情况。

adb shell cmd app_hibernation set-state  true && 
  adb shell cmd app_hibernation set-state --global  true

4.2.10 通知 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 应用兼容性标志启用和停用此限制。

编辑:yimen,如若转载,请注明出处:https://www.yimenapp.com/kb-yimen/12730/

部分内容来自网络投稿,如有侵权联系立删

(0)
上一篇 2022年11月24日 下午3:44
下一篇 2022年11月24日 下午3:47

相关推荐