在 Android 9(及更低版本)中,应用会在以下情况下进入 PAUSED
状态:
- 一项新的半透明 Activity 在应用的上层启动,同时应用仍然可见(因此并未停止)。
- 该 Activity 失去了焦点,但没有被遮挡,可以与用户进行互动。例如,在多窗口模式下,一些 Activity 处于可见状态,并可同时接收触摸输入。
这些情况的不同之处在于应用必须执行的暂停次数,但无法在应用级别进行区分。
在 Android 10 中,可见堆栈中所有可设置为焦点的顶层 Activity 都处于 RESUMED
状态。对于使用 onPause()
(而非 onStop()
)来停止刷新界面及与用户互动的应用而言,这样可提高与多窗口模式和多屏幕 (MD) 模式的兼容性。这意味着:
- 在分屏模式下,两个 Activity 都处于 RESUMED 状态。
- 在自由格式的窗口模式下,所有可见的顶层 Activity 都处于 RESUMED 状态。
- 多个屏幕上的 Activity 可以同时处于 RESUMED 状态。
图 1. 可折叠设备上的多项恢复
图 2. 在桌面模式下的多项恢复
当 activity 无法设置为焦点或被部分遮挡时(示例如下),它们可以处于 PAUSED
状态:
- 在已最小化的分屏(启动器在侧面)模式下,顶层 Activity 不处于 RESUMED 状态,因为它无法设置为焦点。
- 在画中画模式下,Activity 不处于 RESUMED 状态,因为它无法设置为焦点。
- 当 Activity 被同一堆栈中的其他透明 Activity 覆盖时。
在此方式下,对应用而言,Activity 仅在 RESUMED
状态下才能接收来自用户的输入。在 Android 10 之前,Activity 也可以在 PAUSED
状态下接收输入(例如,您可以试试在搭载 Android 9 的设备上同时轻触分屏模式下的两个 Activity)。
为了保留之前 Android 版本的“已恢复”信号(并告知应用应在何时获取对独占资源或单例资源的访问权),Android 10 新增了一个回调:
Activity#onTopResumedActivityChanged(boolean onTop)
调用后,此回调将在 Activity#onResume()
和 Activity#onPause()
之间实际得到调用。此回调并非必需且可以跳过,因此,Activity 可以从 RESUMED
变为 PAUSED
状态,而不会处于系统的最顶层,如在多窗口模式下。由于此回调并非必需,因此它不是 Activity 生命周期的一部分,应很少使用。
上一个在顶层处于 RESUMED 状态的 Activity 先接收 onTopResumedActivity(false)
并完成其执行过程,然后下一个在顶层处于 RESUMED 状态的 Activity 才会接收 onTopResumedActivity(true)
,除非上一个 Activity 在处理该方法调用时花费了过长时间并达到了 500ms 超时。
兼容性
如要在实现多项恢复时保持兼容性,请考虑以下解决方案。
在一个应用进程中启动多个处于已恢复状态的 Activity
- 问题:在 Android 9 及更低版本中,系统同时只允许一个 Activity 处于 RESUMED 状态。多个 Activity 执行任何转换时,都需要先暂停一个 Activity,然后再恢复另一个 Activity。一些应用和框架(例如 Flutter,或 Android 的 LocalActivityManager)会利用此行为方式,并在单例中存储已恢复的 Activity 的相关状态。
- 解决方案:在 Android 9 及更低版本中,如果同一进程中的两个 Activity 都要进行恢复,系统仅会恢复在 Z 轴顺序中较高的 Activity。以 Android 10 为目标平台的应用可支持同时恢复多个 Activity。
同时访问相机
- 问题:这些问题在 Android 9 及更低版本中也存在。例如,在画中画模式下,处于 RESUMED 状态的全屏 Activity 可能会丢失相机焦点,而在顶层处于 RESUMED 状态的 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 中,“活跃”意味着应用处于 RESUMED
或 PAUSED
状态,且仅适用于这两种情况。
在 Android 10 中,恢复 Activity 的过程将在每个堆栈中(而不是系统中的单个位置)单独进行跟踪。这是因为多窗口模式下可以同时执行多个 Activity 转换。如需了解详情,请参阅 ActivityStack#mInResumeTopActivity
。
在顶层处于已恢复状态的 activity 回调
在执行可能导致顶层 activity 发生更改的操作(例如启动 activity、恢复或更改 Z 轴顺序)之后,系统将调用 ActivityStackSupervisor#updateTopResumedActivityIfNeeded()
。此方法会检查在顶层处于已恢复状态的 activity 是否已更改,并在需要时执行更新。如果上一个在顶层处于已恢复状态的 activity 尚未释放这一状态,系统会向其发送一条告知其失去该状态的消息,并在服务器端安排一项超时操作 (ActivityStackSupervisor#scheduleTopResumedStateLossTimeout()
)。在该 activity 释放这一状态或发生超时后,系统会将该状态授予下一个 activity。用法如下:
ActivityStackSupervisor#scheduleTopResumedActivityStateIfNeeded()
Android 10 新增了一项 TopResumedActivityChangeItem
事务,该事务用于向客户端报告在顶层处于已恢复状态的更改,并利用了 Android 9 中的 ActivityLifecycler
架构。
该状态的相关信息存储在客户端,每次 Activity 转换为 RESUMED
或 PAUSED
时,系统还会检查是否应该调用 onTopResumedActivityChanged()
回调。这可以在服务器和客户端之间就生命周期状态和“在顶层处于 RESUMED 状态”这一状态进行通信时实现某些分离。