Activity 生命周期

当用户在您的应用中导航、离开应用并返回时,您应用中的 Activity 实例会经历不同的生命周期状态。Activity 类提供了许多回调,用于通知 Activity 何时状态发生变化,或系统正在创建、停止或恢复 Activity,或者销毁 Activity 所在的进程。

在生命周期回调方法中,您可以声明当用户离开 Activity 并重新进入时,您的 Activity 将如何行为。例如,如果您正在构建流媒体视频播放器,当用户切换到另一个应用时,您可能会暂停视频并终止网络连接。当用户返回时,您可以重新连接到网络并让用户从同一位置恢复视频。

每个回调都允许您执行适合给定状态更改的特定工作。在正确的时间执行正确的工作并正确处理过渡,可以使您的应用更健壮、性能更好。例如,良好地实现生命周期回调可以帮助您的应用避免以下情况:

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

本文档详细介绍了 Activity 生命周期。文档首先描述了生命周期范例。接下来,它解释了每个回调:它们执行时内部发生了什么以及您在其中需要实现什么。

然后,它简要介绍了 Activity 状态与进程易受系统终止的关系。最后,它讨论了几个与 Activity 状态之间过渡相关的主题。

有关处理生命周期的信息,包括最佳实践指南,请参阅 使用感知生命周期的组件处理生命周期 保存 UI 状态。要了解如何结合使用 Activity 和架构组件来构建健壮的生产质量应用,请参阅 应用架构指南

Activity 生命周期概念

为了管理 Activity 生命周期阶段之间的过渡,Activity 类提供了一组核心的六个回调:onCreate()onStart()onResume()onPause()onStop()onDestroy()。当 Activity 进入新状态时,系统会调用这些回调。

图 1 展示了此范例的视觉表示。

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

当用户开始离开 Activity 时,系统会调用方法来卸载 Activity。在某些情况下,Activity 仅被部分卸载并仍驻留在内存中,例如当用户切换到另一个应用时。在这些情况下,Activity 仍可以返回到前台。

如果用户返回 Activity,它会从用户离开的地方恢复。除了少数例外情况,应用在后台运行时启动 Activity 会受到限制

系统终止给定进程及其 Activity 的可能性,取决于 Activity 在当时的状态。有关状态与被逐出内存之间的关系,请参阅Activity 状态和从内存中逐出一节。

根据您的 Activity 的复杂程度,您可能不需要实现所有生命周期方法。但是,重要的是您要理解每个方法,并实现那些能让您的应用按照用户预期方式运行的方法。

生命周期回调

本节提供了有关 Activity 生命周期期间使用的回调方法的概念和实现信息。

某些操作属于 Activity 生命周期方法。但是,请将实现依赖组件操作的代码放在组件中,而不是 Activity 生命周期方法中。为此,您需要使依赖组件感知生命周期。要了解如何使您的依赖组件感知生命周期,请参阅 使用感知生命周期的组件处理生命周期

onCreate()

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

例如,您的 onCreate() 实现可能将数据绑定到列表,将 Activity 与 ViewModel 关联,并实例化一些类作用域变量。此方法接收参数 savedInstanceState,它是一个包含 Activity 先前保存状态的 Bundle 对象。如果 Activity 以前从未存在过,则 Bundle 对象的值为 null。

如果您有连接到 Activity 生命周期的感知生命周期的组件,它会收到 ON_CREATE 事件。带有 @OnLifecycleEvent 注解的方法会被调用,以便您的感知生命周期的组件可以执行创建状态所需的任何设置代码。

以下 onCreate() 方法的示例展示了 Activity 的基本设置,例如声明用户界面(在 XML 布局文件中定义)、定义成员变量以及配置部分 UI。在此示例中,XML 布局文件将文件的资源 ID R.layout.main_activity 传递给 setContentView()

Kotlin

lateinit var textView: TextView

// Some transient state for the activity instance.
var gameState: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
    // Call the superclass onCreate to complete the creation of
    // the activity, like the view hierarchy.
    super.onCreate(savedInstanceState)

    // Recover the instance state.
    gameState = savedInstanceState?.getString(GAME_STATE_KEY)

    // Set the user interface layout for this activity.
    // The layout is defined in the project res/layout/main_activity.xml file.
    setContentView(R.layout.main_activity)

    // Initialize member TextView so it is available later.
    textView = findViewById(R.id.text_view)
}

// This callback is called only when there is a saved instance previously saved using
// onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally
// be restored here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    textView.text = savedInstanceState?.getString(TEXT_VIEW_KEY)
}

// Invoked when the activity might be temporarily destroyed; save the instance state here.
override fun onSaveInstanceState(outState: Bundle?) {
    outState?.run {
        putString(GAME_STATE_KEY, gameState)
        putString(TEXT_VIEW_KEY, textView.text.toString())
    }
    // Call superclass to save any view hierarchy.
    super.onSaveInstanceState(outState)
}

Java

TextView textView;

// Some transient state for the activity instance.
String gameState;

@Override
public void onCreate(Bundle savedInstanceState) {
    // Call the superclass onCreate to complete the creation of
    // the activity, like the view hierarchy.
    super.onCreate(savedInstanceState);

    // Recover the instance state.
    if (savedInstanceState != null) {
        gameState = savedInstanceState.getString(GAME_STATE_KEY);
    }

    // Set the user interface layout for this activity.
    // The layout is defined in the project res/layout/main_activity.xml file.
    setContentView(R.layout.main_activity);

    // Initialize member TextView so it is available later.
    textView = (TextView) findViewById(R.id.text_view);
}

// This callback is called only when there is a saved instance previously saved using
// onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally
// be restored here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
    textView.setText(savedInstanceState.getString(TEXT_VIEW_KEY));
}

// Invoked when the activity might be temporarily destroyed; save the instance state here.
@Override
public void onSaveInstanceState(Bundle outState) {
    outState.putString(GAME_STATE_KEY, gameState);
    outState.putString(TEXT_VIEW_KEY, textView.getText());

    // Call superclass to save any view hierarchy.
    super.onSaveInstanceState(outState);
}

作为定义 XML 文件并将其传递给 setContentView() 的替代方案,您可以在 Activity 代码中创建新的 View 对象,并通过将新的 View 对象插入到 ViewGroup 中来构建视图层次结构。然后,通过将根 ViewGroup 传递给 setContentView() 来使用该布局。有关创建用户界面的更多信息,请参阅 用户界面文档。

您的 Activity 不会停留在已创建 (Created) 状态。在 onCreate() 方法执行完毕后,Activity 进入*已启动 (Started)* 状态,系统会快速连续地调用 onStart()onResume() 方法。

onStart()

当 Activity 进入已启动 (Started) 状态时,系统会调用 onStart()。此调用使 Activity 对用户可见,因为应用正在准备 Activity 进入前台并变得可交互。例如,此方法是初始化维护 UI 的代码的地方。

当 Activity 进入已启动 (Started) 状态时,任何与 Activity 生命周期绑定的感知生命周期的组件都会收到 ON_START 事件。

onStart() 方法会快速完成,并且与 Created 状态一样,Activity 不会停留在 Started 状态。此回调完成后,Activity 进入*已恢复 (Resumed)* 状态,系统会调用 onResume() 方法。

onResume()

当 Activity 进入已恢复 (Resumed) 状态时,它会来到前台,系统会调用 onResume() 回调。这是应用与用户交互的状态。应用会一直停留在该状态,直到发生某些事情导致焦点从应用中移开,例如设备接到电话、用户导航到另一个 Activity 或设备屏幕关闭。

当 Activity 进入已恢复 (Resumed) 状态时,任何与 Activity 生命周期绑定的感知生命周期的组件都会收到 ON_RESUME 事件。生命周期组件可以在此处启用组件可见且位于前台时需要运行的任何功能,例如启动相机预览。

当发生中断性事件时,Activity 进入*已暂停 (Paused)* 状态,系统会调用 onPause() 回调。

如果 Activity 从已暂停 (Paused) 状态返回到已恢复 (Resumed) 状态,系统会再次调用 onResume() 方法。因此,请实现 onResume() 以初始化在 onPause() 期间释放的组件,并执行每次 Activity 进入 Resumed 状态时必须发生的任何其他初始化。

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

Kotlin

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

Java

public class CameraComponent implements LifecycleObserver {

    ...

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void initializeCamera() {
        if (camera == null) {
            getCamera();
        }
    }
    ...
}

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

如果您只想在应用处于已恢复 (Resumed) 状态(可见且在前台活跃)时相机处于活动状态,那么请在前面演示的 ON_RESUME 事件之后初始化相机。如果您希望在 Activity 处于已暂停 (Paused) 状态但可见时(例如在多窗口模式下)保持相机活跃,那么请在 ON_START 事件之后初始化相机。

但是,在 Activity 暂停时保持相机活跃可能会阻止多窗口模式下另一个已恢复 (Resumed) 应用访问相机。有时在 Activity 暂停时保持相机活跃是必要的,但这样做实际上可能会降低整体用户体验。

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

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

上述代码片段将相机初始化代码放置在感知生命周期的组件中。您可以选择将此代码直接放入 Activity 生命周期回调中,例如 onStart()onStop(),但我们不推荐这样做。将此逻辑添加到独立的感知生命周期的组件中,您可以跨多个 Activity 重用该组件,而无需复制代码。要了解如何创建感知生命周期的组件,请参阅 使用感知生命周期的组件处理生命周期

onPause()

系统调用此方法作为用户正在离开您的 Activity 的第一个指示,尽管这并不总是意味着 Activity 正在被销毁。它表示 Activity 不再位于前台,但如果用户处于多窗口模式,它仍然可见。Activity 进入此状态有几个原因:

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

当 Activity 进入已暂停 (Paused) 状态时,任何与 Activity 生命周期绑定的感知生命周期的组件都会收到 ON_PAUSE 事件。生命周期组件可以在此处停止组件不在前台时不需要运行的任何功能,例如停止相机预览。

使用 onPause() 方法来暂停或调整在 Activity 处于已暂停 (Paused) 状态时无法继续进行或可能适度继续进行的操作,并且您期望其很快恢复。

您还可以使用 onPause() 方法来释放系统资源、传感器句柄(如 GPS)或任何在 Activity 处于已暂停 (Paused) 状态且用户不需要时影响电池续航的资源。

然而,正如关于 onResume() 的章节所述,如果应用处于多窗口模式,已暂停 (Paused) 的 Activity 仍可能完全可见。考虑使用 onStop() 而不是 onPause() 来完全释放或调整与 UI 相关的资源和操作,以更好地支持多窗口模式。

以下是 LifecycleObserverON_PAUSE 事件做出反应的示例,它与前面的 ON_RESUME 事件示例相对应,在收到 ON_RESUME 事件后释放已初始化的相机。

Kotlin

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

Java

public class JavaCameraComponent implements LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void releaseCamera() {
        if (camera != null) {
            camera.release();
            camera = null;
        }
    }
    ...
}

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

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

相反,请在 onStop() 期间执行高负载的关机操作。有关在 onStop() 期间执行的合适操作的更多信息,请参阅下一节。有关保存数据的更多信息,请参阅关于 保存和恢复状态的章节。

onPause() 方法的完成并不意味着 Activity 离开已暂停 (Paused) 状态。相反,Activity 会一直停留在该状态,直到 Activity 恢复或对用户完全不可见。如果 Activity 恢复,系统会再次调用 onResume() 回调。

如果 Activity 从已暂停 (Paused) 状态返回到已恢复 (Resumed) 状态,系统会将 Activity 实例保留在内存中,并在系统调用 onResume() 时重新调用该实例。在这种情况下,您无需重新初始化在任何导致 Resumed 状态的回调方法期间创建的组件。如果 Activity 完全不可见,系统会调用 onStop()

onStop()

当您的 Activity 对用户不再可见时,它进入*已停止 (Stopped)* 状态,系统会调用 onStop() 回调。这可能发生在新启动的 Activity 覆盖整个屏幕时。当 Activity 运行结束并即将终止时,系统也会调用 onStop()

当 Activity 进入已停止 (Stopped) 状态时,任何与 Activity 生命周期绑定的感知生命周期的组件都会收到 ON_STOP 事件。生命周期组件可以在此处停止组件在屏幕上不可见时不需要运行的任何功能。

onStop() 方法中,释放或调整应用对用户不可见时不需要的资源。例如,您的应用可能会暂停动画或从精细位置更新切换到粗略位置更新。使用 onStop() 而不是 onPause() 意味着与 UI 相关的工作将继续,即使当用户在多窗口模式下查看您的 Activity 时也是如此。

此外,使用 onStop() 执行相对耗费 CPU 的关机操作。例如,如果您找不到更好的时间将信息保存到数据库中,您可以在 onStop() 期间进行。以下示例展示了 onStop() 的实现,它将草稿笔记的内容保存到持久性存储中:

Kotlin

override fun onStop() {
    // Call the superclass method first.
    super.onStop()

    // Save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    val values = ContentValues().apply {
        put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText())
        put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle())
    }

    // Do this update in background on an AsyncQueryHandler or equivalent.
    asyncQueryHandler.startUpdate(
            token,     // int token to correlate calls
            null,      // cookie, not used here
            uri,       // The URI for the note to update.
            values,    // The map of column names and new values to apply to them.
            null,      // No SELECT criteria are used.
            null       // No WHERE columns are used.
    )
}

Java

@Override
protected void onStop() {
    // Call the superclass method first.
    super.onStop();

    // Save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    ContentValues values = new ContentValues();
    values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
    values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());

    // Do this update in background on an AsyncQueryHandler or equivalent.
    asyncQueryHandler.startUpdate (
            mToken,  // int token to correlate calls
            null,    // cookie, not used here
            uri,    // The URI for the note to update.
            values,  // The map of column names and new values to apply to them.
            null,    // No SELECT criteria are used.
            null     // No WHERE columns are used.
    );
}

上述代码示例直接使用了 SQLite。但是,我们建议使用 Room,这是一个在 SQLite 之上提供抽象层的持久性库。要了解有关使用 Room 的好处以及如何在应用中实现 Room 的更多信息,请参阅 Room 持久性库指南。

当您的 Activity 进入已停止 (Stopped) 状态时,Activity 对象会驻留在内存中:它维护所有状态和成员信息,但未附加到窗口管理器。当 Activity 恢复时,它会重新调用此信息。

您无需重新初始化在任何导致 Resumed 状态的回调方法期间创建的组件。系统还会跟踪布局中每个 View 对象的当前状态,因此如果用户在 EditText 小部件中输入文本,该内容会保留下来,您无需保存和恢复它。

注意:一旦您的 Activity 停止,如果系统需要回收内存,它可能会销毁包含该 Activity 的进程。即使系统在 Activity 停止时销毁了该进程,系统仍会将 View 对象的(例如 EditText 小部件中的文本)状态保存在 Bundle(一个键值对的 blob)中,如果用户导航回 Activity,系统会恢复它们。有关恢复用户返回的 Activity 的更多信息,请参阅关于 保存和恢复状态的章节。

从已停止 (Stopped) 状态,Activity 要么回来与用户交互,要么 Activity 运行结束并消失。如果 Activity 返回,系统会调用 onRestart()。如果 Activity 运行结束,系统会调用 onDestroy()

onDestroy()

onDestroy() 在 Activity 销毁之前被调用。系统调用此回调的原因有以下两种:

  1. Activity 正在结束,原因可能是用户完全关闭了 Activity,或者在 Activity 上调用了 finish()
  2. 由于配置更改(例如设备旋转或进入多窗口模式),系统正在暂时销毁 Activity。

当 Activity 进入销毁状态时,任何与 Activity 生命周期绑定的感知生命周期的组件都会收到 ON_DESTROY 事件。生命周期组件可以在此处清理在 Activity 销毁之前需要清理的任何内容。

您无需在 Activity 中编写逻辑来确定其被销毁的原因,而是使用 ViewModel 对象来包含您的 Activity 的相关视图数据。如果 Activity 由于配置更改而重新创建,ViewModel 无需执行任何操作,因为它会被保留并传递给下一个 Activity 实例。

如果 Activity 未重新创建,则会调用 ViewModelonCleared() 方法,在该方法中它可以在销毁之前清理任何需要清理的数据。您可以使用 isFinishing() 方法区分这两种情况。

如果 Activity 正在结束,onDestroy() 是 Activity 接收到的最后一个生命周期回调。如果 onDestroy() 是由于配置更改而调用,系统会立即创建一个新的 Activity 实例,然后在新的配置中对该新实例调用 onCreate()

onDestroy() 回调会释放所有未被早期回调(例如 onStop())释放的资源。

Activity 状态和从内存中逐出

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

被终止的可能性 进程状态 最终 Activity 状态
最低 前台(拥有或即将获得焦点) 已恢复
可见(无焦点) 已启动/已暂停
较高 后台(不可见) 已停止
最高 已销毁

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

系统从不直接终止 Activity 以释放内存。相反,它会终止 Activity 运行所在的进程,这不仅会销毁 Activity,还会销毁该进程中运行的所有其他内容。要了解在系统发起的进程终止发生时如何保存和恢复 Activity 的 UI 状态,请参阅关于 保存和恢复状态的章节。

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

有关进程的更多信息,请参阅 进程和线程概览

保存和恢复瞬态 UI 状态

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

同样,用户期望 UI 状态保持不变,如果他们暂时从您的应用切换到另一个应用,然后稍后返回您的应用。然而,当用户离开且您的 Activity 已停止时,系统可能会销毁您的应用的进程。

当系统限制导致 Activity 销毁时,请结合使用 ViewModel onSaveInstanceState() 和/或本地存储来保存用户的瞬态 UI 状态。要了解有关用户期望与系统行为的对比以及如何在系统发起的 Activity 和进程终止时最好地保存复杂的 UI 状态数据的更多信息,请参阅 保存 UI 状态

本节概述了实例状态是什么以及如何实现 onSaveInstance() 方法,该方法是 Activity 自身的一个回调。如果您的 UI 数据是轻量级的,您可以单独使用 onSaveInstance() 来在配置更改和系统发起的进程终止时持久化 UI 状态。但是,由于 onSaveInstance() 会产生序列化/反序列化开销,在大多数情况下,您会同时使用 ViewModelonSaveInstance(),如 保存 UI 状态中所述。

注意:要详细了解配置更改、如何在需要时限制 Activity 重建,以及如何从视图系统和 Jetpack Compose 对这些配置更改做出反应,请查看 处理配置更改页面。

实例状态

有几种情况下,您的 Activity 会由于正常的应用行为而被销毁,例如当用户按下返回按钮或您的 Activity 通过调用 finish() 方法来指示其自身销毁。

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

然而,如果系统由于系统限制(例如配置更改或内存压力)而销毁 Activity,那么尽管实际的 Activity 实例已消失,系统仍然会记住它曾经存在。如果用户尝试导航回 Activity,系统会使用一组描述 Activity 销毁时状态的已保存数据来创建该 Activity 的新实例。

系统用于恢复先前状态的已保存数据称为*实例状态*。它是在 Bundle 对象中存储的键值对集合。默认情况下,系统使用 Bundle 实例状态来保存有关 Activity 布局中每个 View 对象的信息,例如输入到 EditText 小部件中的文本值。

因此,如果您的 Activity 实例被销毁并重新创建,布局的状态将恢复到其先前的状态,无需您编写任何代码。但是,您的 Activity 可能有更多您希望恢复的状态信息,例如跟踪用户在 Activity 中进度的成员变量。

注意: 为了让 Android 系统恢复您的 Activity 中视图的状态,每个视图都必须具有由 android:id 属性提供的唯一 ID。

Bundle 对象不适合保存微不足道的数据量,因为它需要在主线程上进行序列化并消耗系统进程内存。要保存超过非常少量的数据,请采用组合方法来保存数据,使用持久性本地存储、onSaveInstanceState() 方法和 ViewModel 类,如 保存 UI 状态中所述。

使用 onSaveInstanceState() 保存简单、轻量级的 UI 状态

当您的 Activity 开始停止时,系统会调用 onSaveInstanceState() 方法,以便您的 Activity 可以将状态信息保存到实例状态 Bundle 中。此方法的默认实现保存有关 Activity 视图层次结构状态的瞬态信息,例如 EditText 小部件中的文本或 ListView 小部件的滚动位置。

要为您的 Activity 保存额外的实例状态信息,请重写 onSaveInstanceState() 并将键值对添加到 Bundle 对象中,该对象将在您的 Activity 意外销毁时保存。当您重写 onSaveInstanceState() 时,如果您希望默认实现保存视图层次结构的状态,则需要调用超类实现。示例如下:

Kotlin

override fun onSaveInstanceState(outState: Bundle?) {
    // Save the user's current game state.
    outState?.run {
        putInt(STATE_SCORE, currentScore)
        putInt(STATE_LEVEL, currentLevel)
    }

    // Always call the superclass so it can save the view hierarchy state.
    super.onSaveInstanceState(outState)
}

companion object {
    val STATE_SCORE = "playerScore"
    val STATE_LEVEL = "playerLevel"
}

Java

static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
// ...


@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user's current game state.
    savedInstanceState.putInt(STATE_SCORE, currentScore);
    savedInstanceState.putInt(STATE_LEVEL, currentLevel);

    // Always call the superclass so it can save the view hierarchy state.
    super.onSaveInstanceState(savedInstanceState);
}

注意: 当用户明确关闭 Activity 或在其他调用 finish() 的情况下,不会调用 onSaveInstanceState()

要保存持久性数据,例如用户偏好设置或数据库数据,请在 Activity 处于前台时抓住适当的机会。如果此类机会没有出现,请在 onStop() 方法期间保存持久性数据。

使用已保存的实例状态恢复 Activity UI 状态

当您的 Activity 在之前被销毁后重新创建时,您可以从系统传递给您的 Activity 的 Bundle 中恢复您已保存的实例状态。onCreate() onRestoreInstanceState() 回调方法都会收到包含实例状态信息的相同 Bundle

由于无论系统是创建您的 Activity 的新实例还是重新创建以前的实例,都会调用 onCreate() 方法,因此在尝试读取状态 Bundle 之前,您需要检查它是否为 null。如果它为 null,则系统正在创建 Activity 的新实例,而不是恢复先前被销毁的实例。

以下代码片段展示了如何在 onCreate() 中恢复某些状态数据:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState) // Always call the superclass first

    // Check whether we're recreating a previously destroyed instance.
    if (savedInstanceState != null) {
        with(savedInstanceState) {
            // Restore value of members from saved state.
            currentScore = getInt(STATE_SCORE)
            currentLevel = getInt(STATE_LEVEL)
        }
    } else {
        // Probably initialize members with default values for a new instance.
    }
    // ...
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); // Always call the superclass first

    // Check whether we're recreating a previously destroyed instance.
    if (savedInstanceState != null) {
        // Restore value of members from saved state.
        currentScore = savedInstanceState.getInt(STATE_SCORE);
        currentLevel = savedInstanceState.getInt(STATE_LEVEL);
    } else {
        // Probably initialize members with default values for a new instance.
    }
    // ...
}

您可以选择实现 onRestoreInstanceState() 而不是在 onCreate() 期间恢复状态,系统会在 onStart() 方法之后调用它。系统仅在存在要恢复的已保存状态时才调用 onRestoreInstanceState(),因此您无需检查 Bundle 是否为 null。

Kotlin

override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    // Always call the superclass so it can restore the view hierarchy.
    super.onRestoreInstanceState(savedInstanceState)

    // Restore state members from saved instance.
    savedInstanceState?.run {
        currentScore = getInt(STATE_SCORE)
        currentLevel = getInt(STATE_LEVEL)
    }
}

Java

public void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy.
    super.onRestoreInstanceState(savedInstanceState);

    // Restore state members from saved instance.
    currentScore = savedInstanceState.getInt(STATE_SCORE);
    currentLevel = savedInstanceState.getInt(STATE_LEVEL);
}

注意:务必调用 onRestoreInstanceState() 的超类实现,以便默认实现可以恢复视图层次结构的状态。

Activity 之间的导航

在应用的生命周期中,应用可能会多次进入和退出 Activity,例如当用户点击设备的返回按钮或 Activity 启动不同的 Activity 时。

本节涵盖了您需要了解的实现成功的 Activity 过渡的主题。这些主题包括从另一个 Activity 启动 Activity、保存 Activity 状态和恢复 Activity 状态。

从另一个 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。

Kotlin

val intent = Intent(this, SignInActivity::class.java)
startActivity(intent)

Java

Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);

您的应用可能还希望执行一些操作,例如使用来自您的 Activity 的数据发送电子邮件、短信或状态更新。在这种情况下,您的应用可能没有自己的 Activity 来执行此类操作,因此您可以转而利用设备上其他应用提供的 Activity,它们可以为您执行这些操作。

这就是 Intent 真正有价值的地方。您可以创建一个描述您想要执行的操作的 Intent,系统会从另一个应用中启动相应的 Activity。如果存在多个可以处理该 Intent 的 Activity,则用户可以选择使用哪个。例如,如果您想让用户发送电子邮件,您可以创建以下 Intent:

Kotlin

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

Java

Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);

添加到 Intent 的 EXTRA_EMAIL extra 是一个字符串数组,包含要发送电子邮件的地址。当电子邮件应用响应此 Intent 时,它会读取 extra 中提供的字符串数组,并将地址放入电子邮件撰写表单的“收件人”字段。在这种情况下,电子邮件应用的 Activity 会启动,当用户完成后,您的 Activity 会恢复。

startActivityForResult()

有时您希望在 Activity 结束时从 Activity 获取结果。例如,您可能会启动一个 Activity,让用户从联系人列表中选择一个人。当它结束时,它会返回所选的人员。为此,您需要调用 startActivityForResult(Intent, int) 方法,其中整数参数标识了该调用。

此标识符旨在区分来自同一 Activity 的对 startActivityForResult(Intent, int) 的多次调用。它不是全局标识符,并且没有与其他应用或 Activity 冲突的风险。结果通过您的 onActivityResult(int, int, Intent) 方法返回。

当子 Activity 退出时,它可以调用 setResult(int) 将数据返回给其父级。子 Activity 必须提供一个结果代码,该代码可以是标准结果 RESULT_CANCELEDRESULT_OK,或从 RESULT_FIRST_USER 开始的任何自定义值。

此外,子 Activity 可以选择返回一个包含其所需任何额外数据的 Intent 对象。父 Activity 使用 onActivityResult(int, int, Intent) 方法以及父 Activity 最初提供的整数标识符来接收信息。

如果子 Activity 因任何原因失败,例如崩溃,父 Activity 将收到代码为 RESULT_CANCELED 的结果。

Kotlin

class MyActivity : Activity() {
    // ...

    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
            // When the user center presses, let them pick a contact.
            startActivityForResult(
                    Intent(Intent.ACTION_PICK,Uri.parse("content://contacts")),
                    PICK_CONTACT_REQUEST)
            return true
        }
        return false
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
        when (requestCode) {
            PICK_CONTACT_REQUEST ->
                if (resultCode == RESULT_OK) {
                    // A contact was picked. Display it to the user.
                    startActivity(Intent(Intent.ACTION_VIEW, intent?.data))
                }
        }
    }

    companion object {
        internal val PICK_CONTACT_REQUEST = 0
    }
}

Java

public class MyActivity extends Activity {
     // ...

     static final int PICK_CONTACT_REQUEST = 0;

     public boolean onKeyDown(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
             // When the user center presses, let them pick a contact.
             startActivityForResult(
                 new Intent(Intent.ACTION_PICK,
                 new Uri("content://contacts")),
                 PICK_CONTACT_REQUEST);
            return true;
         }
         return false;
     }

     protected void onActivityResult(int requestCode, int resultCode,
             Intent data) {
         if (requestCode == PICK_CONTACT_REQUEST) {
             if (resultCode == RESULT_OK) {
                 // A contact was picked. Display it to the user.
                 startActivity(new Intent(Intent.ACTION_VIEW, data));
             }
         }
     }
 }

协调 Activity

当一个 Activity 启动另一个 Activity 时,它们都会经历生命周期过渡。第一个 Activity 停止运行并进入已暂停 (Paused) 或已停止 (Stopped) 状态,而另一个 Activity 则被创建。如果这些 Activity 共享保存到磁盘或其他地方的数据,重要的是要理解第一个 Activity 在第二个 Activity 创建之前并不会完全停止。相反,启动第二个 Activity 的过程与停止第一个 Activity 的过程是重叠的。

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

  1. Activity A 的 onPause() 方法执行。
  2. Activity B 的 onCreate()onStart()onResume() 方法按顺序执行。Activity B 现在获得用户焦点。
  3. 如果 Activity A 在屏幕上不再可见,其 onStop() 方法将执行。

这种生命周期回调序列允许您管理信息从一个 Activity 到另一个 Activity 的过渡。