多重恢复

在 Android 9(及更低版本)中,当出现以下情况时,应用会进入 PAUSED 状态:

  • 当新的半透明 Activity 在应用顶部启动时,应用仍然可见(因此未停止)。
  • Activity 失去焦点,但未被遮挡,并且用户可以与之互动。例如,在多窗口模式下,多个 Activity 可以同时可见并接收触摸输入。

这些情况在应用必须执行的暂停量方面有所不同,但在应用级别无法区分。

在 Android 10 中,可见堆栈中的所有可聚焦的顶层 Activity 都处于 RESUMED 状态。这提高了与使用 onPause() 而不是 onStop() 来停止刷新 UI 和与用户交互的应用在多窗口和 MD 模式下的兼容性。这意味着:

  • 分屏模式下的两个 Activity 均处于 resumed 状态。
  • 自由窗口模式下的所有顶层可见 Activity 均处于 resumed 状态。
  • 多个屏幕上的 Activity 可以同时处于 resumed 状态。

图 1. 可折叠设备上的多 resumed 功能

图 2. 桌面模式下的多 resumed 功能

当 Activity 无法被聚焦或被部分遮挡时,它们可能处于 PAUSED 状态,例如:

  • 在最小化的分屏模式下(侧边带有启动器),顶层 Activity 不会处于 resumed 状态,因为它不可聚焦。
  • 在画中画模式下,Activity 不会处于 resumed 状态,因为它不可聚焦。
  • 当 Activity 被同一堆栈中的其他透明 Activity 覆盖时。

此方法向应用表明,Activity 只有在 RESUMED 状态下才能接收来自用户的输入。在 Android 10 之前,Activity 也可以在 PAUSED 状态下接收输入(例如,尝试在运行 Android 9 的设备上同时触摸分屏模式下的两个 Activity)。

为了保留之前 Android 版本中的 resumed 信号(并沟通应用何时应获取对独占访问或单例资源的访问权限),Android 10 包含一个新的回调:

Activity#onTopResumedActivityChanged(boolean onTop)

调用此回调时,它会在 Activity#onResume()Activity#onPause() 之间调用。此回调是可选的,可以跳过,因此 Activity 可以从 RESUMED 状态转变为 PAUSED 状态,而无需成为系统中最顶层的 Activity。例如,在多窗口模式下。由于此回调是可选的,因此它不属于 Activity 生命周期,应谨慎使用。

先前的顶层 resumed Activity 会接收并完成执行 onTopResumedActivity(false),然后下一个顶层 resumed Activity 才会接收 onTopResumedActivity(true),除非先前的 Activity 花费太多时间来处理方法调用并达到 500 毫秒的超时时间。

兼容性

为了在实现多 resumed 功能时保持兼容性,请考虑以下解决方案。

一个应用进程中的多个 resumed Activity

  • 问题:在 Android 9 及更低版本中,系统中一次只有一个 Activity 处于 resumed 状态。Activity 之间的所有转换都涉及在恢复另一个 Activity 之前暂停一个 Activity。一些应用和框架(例如 Flutter 或 Android 的 LocalActivityManager)使用此事实,并将有关 resumed Activity 的状态存储在单例中。
  • 解决方案:在 Android 9 及更低版本中,如果来自同一进程的两个 Activity 都处于 resumed 状态,系统只会恢复 Z 轴顺序较高的 Activity。以 Android 10 为目标平台的应用可以支持同时处于 resumed 状态的多个 Activity。

同时访问相机

  • 问题:这些问题也存在于 Android 9 及更低版本中。例如,全屏和 resumed 状态的 Activity 可能会将相机焦点丢失给画中画模式中位于顶层的 paused 状态 Activity,但随着多窗口和多显示模式的更广泛应用,这些问题会更加突出。
    • 由于对 RESUME 状态所做的更改,应用可能会即使在 resumed 状态下也与相机断开连接。为了解决这个问题,应用必须处理相机断开连接的情况,而不会崩溃。断开连接后,应用会收到断开连接回调,并且对 API 的所有调用都会开始抛出 CameraAccessException
    • resizeableActivity=false 并不能保证独占相机访问权限,因为使用相机的其他应用可以在其他显示屏上打开。
  • 解决方案。 开发者应包含应用与相机断开连接时的逻辑。如果应用与相机断开连接,则应监视相机可用性回调,以尝试重新连接并继续使用相机。除了现有的 CameraManager#AvailabilityCallback#onCameraAvailable() 回调之外,Android 10 还添加了 CameraManager#AvailabilityCallback#onCameraAccessPrioritiesChanged(),它涵盖了焦点(和相机优先级)在多个 resumed 状态 Activity 之间切换的情况。应用开发者应使用这两个回调来确定尝试获取相机访问权限的最佳时机。

多重恢复

在 Android 10 中,Activity 生命周期状态由可见性和 Z 轴顺序决定。为了确保 Activity 上可见性更新后的状态正确,并评估哪个生命周期状态适用,请从不同的位置调用 ActivityRecord#makeActiveIfNeeded() 方法。在 Android 10 中,active 意味着 RESUMEDPAUSED,并且仅在这两种情况下有效。

在 Android 10 中,恢复 Activity 的操作在每个堆栈中单独跟踪,而不是在系统中的单个位置跟踪。这是因为在多窗口模式下可以同时执行多个 Activity 转换。有关详细信息,请参阅 ActivityStack#mInResumeTopActivity

顶层 resumed Activity 回调

在可能导致顶层 Activity 更改的操作(例如 Activity 启动、恢复或 Z 轴顺序更改)之后,会调用 ActivityStackSupervisor#updateTopResumedActivityIfNeeded()。此方法检查最顶层 resumed Activity 是否已更改,并在需要时执行更新。如果先前的顶层 resumed Activity 尚未释放顶层 resumed 状态,则会向其发送顶层 resumed 状态丢失消息,并在服务器端安排超时 (ActivityStackSupervisor#scheduleTopResumedStateLossTimeout())。在先前的 Activity 释放状态之后,或者在超时时间到达时,会将顶层 resumed 状态的报告发送到下一个 Activity(请参阅以下用法:

ActivityStackSupervisor#scheduleTopResumedActivityStateIfNeeded()

新的 TopResumedActivityChangeItem 事务项已添加,用于向客户端报告顶层 resumed 状态更改,并利用了 Android 9 中的 ActivityLifecycler 架构。

顶层 resumed 状态存储在客户端,并且每次 Activity 转换为 RESUMEDPAUSED 时,它还会检查是否应调用 onTopResumedActivityChanged() 回调。这实现了服务器端和客户端之间生命周期状态和顶层 resumed 状态通信中的某些解耦。