activity 生命周期

当用户浏览、退出和返回到您的应用时,您应用中的 Activity 实例会在其生命周期的不同状态间转换。Activity 类会提供许多回调,这些回调会让 activity 知晓某个状态何时发生变化,或者系统何时正在创建、停止或恢复某个 activity,或者正在销毁该 activity 所在的进程。

在生命周期回调方法中,您可以声明用户离开和再次进入 activity 时 activity 的行为方式。例如,如果您正构建流媒体视频播放器,当用户切换至另一应用时,您可能要暂停视频或终止网络连接。当用户返回时,您可以重新连接网络并让用户从同一位置继续播放视频。

每个回调都支持您执行适合给定状态变更的特定作业。在合适的时间执行正确的作业,并妥善处理转换,这将提升应用的稳健性和性能。例如,良好的生命周期回调实现有助于防止应用出现以下问题:

  • 当用户在使用应用时接听来电,或切换至另一应用时崩溃。
  • 当用户未主动使用它时,消耗宝贵的系统资源。
  • 当用户离开应用并在稍后返回时,丢失用户的进度。
  • 当屏幕在横向和纵向之间旋转时,崩溃或丢失用户的进度。

本文档将详细介绍 Activity 生命周期。首先介绍生命周期范例。接着介绍每个回调:它们执行时内部发生了什么,以及您需要在执行期间实现什么。

然后,简要介绍 activity 状态与导致进程被系统终止的漏洞之间的关系。最后,讨论与在 activity 状态之间转换相关的若干主题。

如需了解有关处理生命周期的信息(包括最佳实践的相关指导),请参阅 Jetpack Compose 中的生命周期保存界面状态。如需了解如何将 activity 与架构组件结合使用,以构建生产质量的稳健应用,请参阅应用架构指南

Activity 生命周期概念

为了在 activity 生命周期 的各个 阶段 之间导航 转场 ,Activity 类 提供六个 核心 回调 :onCreateonStartonResumeonPauseonStoponDestroy。当 activity 进入新状态时,系统会调用其中每个回调。

图 1 是对此范例的直观展现。

图 1. Activity 生命周期的简化图示。

当用户开始离开 activity 时,系统会调用方法来销毁该 activity。在某些情况下,Activity 只是部分销毁,仍然驻留在内存中,例如当用户切换到另一个应用时。在这些情况下,Activity 仍然可以返回到前台。

如果用户返回到该 activity,它会从用户离开时的位置继续运行。除了少数例外,应用在后台运行时会受到限制,无法启动 activity

系统终止给定进程及其中 Activity 的可能性取决于当时 Activity 的状态。如需详细了解状态与弹出漏洞之间的关系,请参阅有关 activity 状态和从内存中弹出的部分。

根据 Activity 的复杂程度,您可能不需要实现所有生命周期方法。但是,请务必了解每个方法,并实现能够确保应用按用户预期方式运行的方法,这非常重要。

Compose 和生命周期

在 Compose 中,应避免直接在 onStartonResume 等 activity 回调中放置业务逻辑或手动设置观察者。请改用生命周期感知型效应和状态感知型观察器,它们会自动与界面在屏幕上的显示情况保持一致。

  • 生命周期感知型收集:使用 collectAsStateWithLifecycleViewModel 中使用数据流。此 API 会在界面进入“已启动”状态时自动开始收集,并在界面进入后台时停止收集,从而防止不必要的资源消耗。将 flow 作为状态收集后,您可以在发生生命周期事件时使用 LifecycleEffects 运行代码。
  • 逻辑流程:通过使用这些 API,界面可以通过组合树自然地对生命周期状态做出反应,从而确保仅在用户主动与组件互动时执行业务逻辑。

如需详细了解 Compose 和生命周期,请参阅 Jetpack Compose 中的生命周期

生命周期回调

本部分介绍 activity 生命周期中所用回调方法的相关概念及实现信息。

某些操作属于 activity 生命周期方法。不过,用于实现依赖组件操作的代码应放在组件内,而不是 activity 生命周期方法内。为此,您需要使依赖组件具有生命周期感知能力。如需了解如何让依赖组件获得生命周期感知能力,请参阅 Jetpack Compose 中的生命周期

onCreate

您必须实现此回调,它会在系统首次创建 Activity 时触发。Activity 会在创建后进入“已创建”状态。onCreate 方法中,您需执行基本应用启动逻辑,该逻辑在 activity 的整个生命周期中只应发生一次。

例如,onCreate 的实现可能会将数据绑定到列表,将 activity 与 ViewModel 相关联,并实例化某些类作用域变量。此方法会接收 savedInstanceState 参数,后者是包含 activity 先前保存状态的 Bundle 对象。如果 activity 此前未曾存在,Bundle 对象的值为 null。

如果您有一个生命周期感知型组件与您的 activity 生命周期相关联,该组件将收到 ON_CREATE 事件。系统将调用带有 @OnLifecycleEvent 注释的方法,以使您的生命周期感知型组件可以执行已创建状态所需的任何设置代码。

以下示例展示了如何在最简单的 activity 中集成 Text 可组合项:

class ExampleActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent { // In here, we can call composables!
            MaterialTheme {
                Greeting(name = "compose")
            }
        }
    }
}

@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}

您的 activity 不会一直处于“已创建”状态。onCreate 方法完成执行后,activity 进入“已开始”状态,系统会相继调用 onStartonResume 方法。

onStart

当 activity 进入“已开始”状态时,系统会调用 onStart。此调用使 activity 对用户可见,因为应用会为 activity 进入前台并支持互动做准备。例如,维护界面的代码在此方法中进行初始化。

当 activity 进入“已开始”状态时,与 activity 生命周期相关联的所有生命周期感知型组件都会收到 ON_START 事件。

onStart 方法会快速完成,并且与“已创建”状态一样,activity 不会一直处于“已开始”状态。一旦此回调结束,Activity 便会进入“已恢复”状态,系统将调用 onResume 方法。

onResume

当 activity 进入“已恢复”状态时,它会来到前台,然后系统调用 onResume 回调。这是应用与用户互动的状态。应用会一直保持这种状态,直到某些事件发生,让焦点远离应用。此类事件包括设备接到来电、用户导航到另一个 activity,或设备屏幕关闭。

当 activity 进入 Resumed 状态时,与 activity 生命周期相关联的所有生命周期感知型组件都会收到 ON_RESUME 事件。这时,生命周期组件可以启用在组件可见且位于前台时需要运行的任何功能,例如启动相机预览。

当发生中断事件时,Activity 进入“已暂停”状态,系统调用 onPause 回调。

如果 activity 从“已暂停”状态返回“已恢复”状态,系统将再次调用 onResume 方法。因此,请实现 onResume 以初始化在 onPause 期间释放的组件,并执行每次 activity 进入“已恢复”状态时必须完成的任何其他初始化操作。

以下是生命周期感知型组件的示例,该组件在收到 ON_RESUME 事件时访问相机:

class CameraComponent : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun initializeCamera() {
        if (camera == null) {
            getCamera()
        }
    }
    ...
}

上述代码会在 LifecycleObserver 收到 ON_RESUME 事件后初始化相机。不过,在多窗口模式下,即使处于“已暂停”状态,您的 activity 也可能完全可见。例如,当应用处于多窗口模式,并且用户点按不包含您的 activity 的窗口时,您的 activity 会进入“已暂停”状态。

如果您希望相机仅在应用处于“已恢复”(可见且在前台运行)状态时可用,请在收到之前演示的 ON_RESUME 事件后初始化相机。如果您希望在 activity 处于“已暂停”状态但可见时(例如在多窗口模式下)保持相机可用,请在收到 ON_START 事件后初始化相机。

不过,若要让相机在 Activity 处于“已暂停”状态时可用,可能会导致系统在多窗口模式下拒绝其他处于“已恢复”状态的应用访问相机。有时可能有必要让相机在 Activity 处于“已暂停”状态时保持可用,但这样做实际可能会降低整体用户体验。

因此,请仔细考虑在多窗口模式下,在生命周期的哪个阶段最适合控制共享系统资源。如需详细了解如何支持多窗口模式,请参阅支持多窗口模式

无论您选择在哪个构建事件中执行初始化操作,都请务必使用相应的生命周期事件来释放资源。如果您在收到 ON_START 事件后初始化某些内容,请在收到 ON_STOP 事件后释放或终止相应内容。如果您在收到 ON_RESUME 事件后初始化某些内容,请在收到 ON_PAUSE 事件后将其释放。

上述代码段将相机初始化代码放置在生命周期感知型组件中。您也可以直接将此代码放入 activity 生命周期回调(例如 onStartonStop),但我们不建议您这样做。通过将此逻辑添加到独立的生命周期感知型组件中,您可以对多个 activity 重复使用该组件,而无需复制代码。如需了解如何创建生命周期感知型组件,请参阅 Jetpack Compose 中的生命周期

onPause

系统将此方法视为用户将要离开您的 activity 的第一个标志,尽管这并不总是意味着 activity 会被销毁。此方法表示 activity 不再位于前台,但如果用户处于多窗口模式,则 activity 仍然可见。Activity 可能因多种原因而进入此状态:

  • onResume 回调部分所述,中断应用执行的事件会暂停当前 activity。这是最常见的情况。
  • 在多窗口模式下,任何时候都只有一个应用具有焦点,系统会暂停所有其他应用。
  • 当新的半透明 activity(例如对话框)处于开启状态时,它所覆盖的 activity 会暂停。只要 activity 部分可见但未处于焦点之中,它便会一直暂停。

当 activity 进入“已暂停”状态时,与 activity 生命周期相关联的所有生命周期感知型组件都会收到 ON_PAUSE 事件。这时,生命周期组件可以停止在组件未位于前台时无需运行的任何功能,例如停止相机预览。

使用 onPause 方法暂停或调整当 Activity 处于“已暂停”状态时无法继续(或应有节制地继续)的操作,以及您希望很快恢复的操作。

您还可以使用 onPause 方法释放系统资源、传感器(例如 GPS)手柄,或当您的 activity 处于暂停状态且用户不需要它们时仍然会影响电池续航时间的任何资源。

不过,正如 onResume 部分所述,如果应用处于多窗口模式,“已暂停”的 activity 仍可能完全可见。考虑使用 onStop 而不是 onPause 来完全释放或调整与界面相关的资源和操作,以便更好地支持多窗口模式。

以下 LifecycleObserver 响应 ON_PAUSE 事件的示例与上述 ON_RESUME 事件示例相对应,会释放在收到 ON_RESUME 事件后初始化的相机:

class CameraComponent : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun releaseCamera() {
        camera?.release()
        camera = null
    }
    ...
}

此示例将相机释放代码放置在 LifecycleObserver 收到 ON_PAUSE 事件之后。

onPause 执行非常简单,而且不一定要有足够的时间来执行保存操作。因此,请勿使用 onPause 来保存应用或用户数据、进行网络调用或执行数据库事务。此类工作可能无法在方法完成之前完成。

相反,您应在 onStop 期间执行高负载的关闭操作。如需详细了解在 onStop 期间适合执行的操作,请参阅下一部分。如需详细了解如何保存数据,请参阅关于保存和恢复状态的部分。

onPause 方法的完成并不意味着 activity 离开“已暂停”状态。相反,Activity 会保持此状态,直到其恢复或变成对用户完全不可见。如果 activity 恢复,系统将再次调用 onResume 回调。

如果 Activity 从“已暂停”状态返回“已恢复”状态,系统会让 Activity 实例继续驻留在内存中,并会在系统调用 onResume 时重新调用该实例。在这种情况下,您无需重新初始化在任何回调方法导致 Activity 进入“已恢复”状态期间创建的组件。如果 activity 变为完全不可见,系统会调用 onStop

onStop

如果您的 activity 不再对用户可见,说明其已进入“已停止”状态,因此系统将调用 onStop 回调。当新启动的 activity 覆盖整个屏幕时,可能会发生这种情况。如果 activity 已结束运行并即将终止,系统还会调用 onStop

当 activity 进入已停止状态时,与 activity 生命周期相关联的所有生命周期感知型组件都会收到 ON_STOP 事件。这时,生命周期组件可以停止在组件未显示在屏幕上时无需运行的任何功能。

onStop 方法中,释放或调整在应用对用户不可见时的无用资源。例如,应用可以暂停动画效果,或从精确位置更新切换到粗略位置更新。使用 onStop 而非 onPause 意味着与界面相关的工作会继续进行,即使用户在多窗口模式下查看您的 activity 也能如此。

此外,还应使用 onStop 执行 CPU 相对密集的关闭操作。例如,如果您无法找到更合适的时机来将信息保存到数据库,可以在 onStop 期间执行此操作。以下示例展示了 onStop 的实现,它将草稿笔记内容保存到持久性存储空间中:

override fun onStop() {
    super.onStop()

    // Delegate the save operation to the ViewModel, which handles the
    // background thread operations (e.g., using Kotlin Coroutines and Room).
    noteViewModel.saveDraft()
}

当您的 activity 进入“已停止”状态时,Activity 对象会继续驻留在内存中:该对象将维护所有状态和成员信息,但不会附加到窗口管理器。当 activity 恢复时,它会重新调用这些信息。

进入“已停止”状态后,Activity 要么返回与用户互动,要么结束运行并消失。如果 activity 返回,系统将调用 onRestart。如果 Activity 结束运行,系统将调用 onDestroy

onDestroy

销毁 activity 之前,系统会先调用 onDestroy。系统调用此回调的原因有两个:

  1. Activity 即将结束,因为用户彻底关闭了该 activity,或者系统为该 activity 调用了 finish
  2. 由于配置变更(例如设备旋转或进入多窗口模式),系统暂时销毁 Activity。

当 activity 进入已销毁状态时,与 activity 生命周期相关联的所有生命周期感知型组件都会收到 ON_DESTROY 事件。这时,生命周期组件可以在 Activity 被销毁之前清理所需的任何数据。

您应使用 ViewModel 对象来包含 Activity 的相关视图数据,而不是在 Activity 中加入逻辑来确定 Activity 被销毁的原因。如果因配置变更而重新创建 ActivityViewModel 不必执行任何操作,因为系统将保留 ViewModel 并将其提供给下一个 Activity 实例。

如果不重新创建 Activity,则 ViewModel 会调用 onCleared 方法,以便在被销毁前清理所需的任何数据。您可以使用 isFinishing 方法区分这两种情况。

如果 activity 即将结束,onDestroy 是 activity 收到的最后一个生命周期回调。如果由于配置变更而调用 onDestroy,系统会立即新建 activity 实例,然后在新配置中为新实例调用 onCreate

onDestroy 回调会释放先前的回调(例如 onStop)尚未释放的所有资源。

Activity 状态和从内存中弹出

系统会在需要释放 RAM 时终止进程。系统终止给定进程的可能性取决于当时进程的状态。反之,进程状态取决于在进程中运行的 activity 的状态。表 1 展示了进程状态、activity 状态以及系统终止进程的可能性之间的关系。此表仅在进程未运行其他类型的应用组件时适用。

系统终止进程的可能性

进程状态

最终 activity 状态

最低

前台(拥有或即将获得焦点)

已恢复

可见(无焦点)

已开始/已暂停

较高

后台(不可见)

已停止

最高

已销毁

表 1. 进程生命周期和 activity 状态之间的关系。

系统永远不会直接终止 Activity 以释放内存,而是会终止 activity 所在的进程。系统不仅会销毁 activity,还会销毁在该进程中运行的所有其他内容。如需了解如何在系统启动的进程被终止时保留和恢复 activity 的界面状态,请参阅有关保存和恢复状态的部分。

用户还可以使用“设置”下的“应用管理器”来终止进程,以终止相应的应用。

如需详细了解进程,请参阅进程和线程概览

保存和恢复瞬时界面状态

用户期望 Activity 的界面状态在整个配置更改(例如旋转或切换到多窗口模式)期间保持不变。不过,默认情况下,系统会在发生此类配置更改时销毁 activity,从而清除存储在 activity 实例中的任何界面状态。

同样,如果用户暂时从您的应用切换到其他应用,并在稍后返回您的应用,他们也希望界面状态保持不变。但是,当用户离开应用且您的 activity 停止时,系统可能会销毁该应用的进程。

当系统限制导致 Activity 被销毁时,请组合使用 ViewModel(用于复杂的业务逻辑和屏幕状态)、Jetpack Compose rememberSaveable API(用于轻量级界面状态)和/或本地存储来保留用户的瞬时界面状态。如需详细了解用户期望与系统行为,以及如何在系统启动的 Activity 和进程终止后最大程度地保留复杂的界面状态数据,请参阅保存界面状态

rememberSaveable 通过在后台捆绑状态,可自动在配置更改和系统发起的进程终止后保持状态,从而提供无缝体验,而无需 activity 级样板代码。

实例状态

在某些情况下,您的 activity 会因正常的应用行为而被销毁,例如当用户按下返回按钮或您的 activity 通过调用 finish 方法发出销毁信号时。

当您的 activity 因用户按下返回按钮或因其自行结束而被销毁时,系统和用户对该 Activity 实例的概念将永远消失。在这些情况下,用户的期望与系统行为相匹配,您无需完成任何额外工作。

但是,如果系统因系统限制(例如配置变更或内存压力)而销毁 activity,虽然实际的 Activity 实例会消失,但系统会记住它曾经存在过。如果用户尝试回退到该 activity,系统将使用一组描述 activity 销毁时状态的已保存数据新建该 activity 的实例。

系统用于恢复先前状态的已保存数据称为实例状态。从底层来看,它是一个键值对集合。默认情况下,系统会使用实例状态来保存有关界面布局的基本信息,例如用户文本输入或滚动位置。

您可以使用 rememberSaveable 挂钩到此系统行为。如果您的 activity 实例被销毁并重新创建,则封装在 rememberSaveable 中的任何界面状态都会自动恢复,而您无需编写任何额外的 activity 级代码。

不过,您的 activity 可能包含您要恢复的更复杂的状态信息,例如用户数据、网络响应或追踪用户进度的成员变量。实例状态机制(以及 rememberSaveable)并不适合保留大量数据,因为它需要在主线程上进行序列化处理并占用系统进程内存。

如需保存大量数据,请组合使用持久性本地存储、ViewModel 类和 Compose 状态提升,正如保存界面状态中所述。

使用 rememberSaveable 保存简单轻量的界面状态

当您的 activity 开始停止时,系统会准备将状态信息保存到实例状态 bundle 中。如需挂接到此系统行为,您可以在可组合函数中直接使用 rememberSaveablerememberSaveable 会在重新创建 activity 时自动保存和恢复瞬时界面状态,例如用户文本输入或滚动位置。

如需保存自定义的轻量级状态信息(例如用户在游戏中的进度),请使用 rememberSaveable 声明状态。Compose 框架会在后台处理序列化到实例状态 bundle 的过程:

var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) {
    mutableStateOf(
        TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length))
    )
}

如需保存持久性数据(例如用户首选项或数据库中的数据),您应在 activity 位于前台时抓住合适机会。如果没有这样的时机,请在执行 onStop 方法期间保存持久性数据。

使用保存的实例状态恢复 Activity 界面状态

在先前被销毁的 activity 重新创建后,状态恢复是自动进行的。使用 rememberSaveable 时,您无需编写任何显式恢复逻辑、检查 null bundle 或替换 activity 回调。用于初始化和保存状态的代码还会在 activity 返回时无缝恢复状态:

var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) {
    mutableStateOf(
        TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length))
    )
}

活动和导航

应用在其生命周期内可能会多次在屏幕之间转换,例如当用户点按设备的“返回”按钮或选择新目的地时。现代 Android 应用通常使用单 activity 架构。您的应用不是为每个界面都启动一个新的 Activity,而是托管一个 Activity,并使用 Navigation 组件来替换该 activity 中的可组合界面。

如需了解如何实现以 Compose 为先的现代导航,请参阅 Jetpack Compose Navigation 3 库指南。

从一个 Activity 启动另一个 Activity

activity 可能需要在某个时刻启动另一个 activity。例如,当应用需要从当前屏幕移动到新屏幕时,就会出现这种需求。

根据您的 activity 是否希望从即将启动的新 activity 中获取返回结果,您可以使用 startActivity 方法或 startActivityForResult 方法启动新 activity。这两种方法都需要传入一个 Intent 对象。

Intent 对象指定您要启动的具体 activity,或描述您要执行的操作类型。系统为您选择相应的 activity,该 activity 甚至可以来自不同应用。Intent 对象还可以携带由已启动的 activity 使用的少量数据。如需详细了解 Intent 类,请参阅 Intent 和 Intent 过滤器

startActivity

如果新启动的 activity 不需要返回结果,当前 activity 可以通过调用 startActivity 方法来启动它。

在自己的应用中工作时,您通常只需启动已知 activity。例如,以下代码段显示如何启动一个名为 SignInActivity 的 activity。

val context = LocalContext.current

Button(onClick = {
    val intent = Intent(context, SignInActivity::class.java)
    context.startActivity(intent)
}) {
    Text("Sign In")
}

启动外部 activity

虽然内部应用导航由 Navigation 处理,但您的 Activity 有时需要启动其他 activity。当您想利用外部应用执行特定操作(例如打开网络浏览器、发送电子邮件或拍照)时,通常会发生这种情况。

为此,您可以使用 Intent 对象来描述您要执行的操作类型,系统会从其他应用启动相应的 activity。

例如,如果您想让用户发送电子邮件,可以创建以下 intent:

val intent = Intent(Intent.ACTION_SEND).apply {
    putExtra(Intent.EXTRA_EMAIL, recipientArray)
}
startActivity(intent)

如果您需要启动外部 activity 并获取结果(例如,请求相机应用拍摄照片并返回图片),请使用新版 Activity 结果 API,而不是已弃用的 startActivityForResult 回调。

协调 Activity

当一个 Activity 启动另一个 Activity 时,它们都会经历生命周期转换。第一个 activity 停止运行并进入“已暂停”或“已停止”状态,同时创建另一个 activity。如果这些 activity 共享保存到磁盘或其他位置的数据,必须要明确第一个 activity 在创建第二个 activity 之前并未完全停止。相反,启动第二个 activity 的过程与停止第一个 activity 的过程重叠。

生命周期回调的顺序已有明确定义,特别是当两个 activity 在同一个进程(即同一个应用)中,并且其中一个要启动另一个时。以下是 Activity A 启动 Activity B 时的操作发生顺序:

  1. Activity A 的 onPause 方法执行。
  2. Activity B 的 onCreateonStartonResume 方法依次执行。Activity B 现在具有用户焦点。
  3. 如果 Activity A 在屏幕上不再显示,其 onStop 方法会执行。

您可以利用这种生命周期回调顺序管理从一个 activity 到另一个 activity 的信息转换。

其他资源

如需详细了解 activity 生命周期,请参阅下列其他资源:

查看内容