活动生命周期

当用户浏览应用、离开应用再返回应用时,应用中的 Activity 实例会在其生命周期的不同状态之间转换。Activity 类提供了许多回调,以便在状态更改、系统创建、停止或恢复 Activity 或销毁 Activity 所在的进程时通知 Activity。

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

每个回调都允许您执行适合给定状态更改的特定工作。在正确的时间执行正确的工作并正确处理转换,可使您的应用更稳定、性能更高。例如,正确实现生命周期回调可帮助您的应用避免以下问题:

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

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

然后,它简要介绍了活动状态与进程被系统终止的脆弱性之间的关系。最后,它讨论了与活动状态之间转换相关的几个主题。

有关处理生命周期的信息,包括最佳实践指南,请参阅使用生命周期感知型组件处理生命周期保存界面状态。要了解如何将 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 生命周期方法中。为此,您需要使依赖组件具有生命周期感知能力。要了解如何使您的依赖组件具有生命周期感知能力,请参阅使用生命周期感知型组件处理生命周期

onCreate()

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

例如,您的 onCreate() 实现可能会将数据绑定到列表,将 Activity 与 ViewModel 关联,并实例化一些类作用域变量。此方法接收参数 savedInstanceState,它是一个 Bundle 对象,包含 Activity 以前保存的状态。如果 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 事件之后释放。

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

onPause()

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

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

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

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

您还可以使用 onPause() 方法来释放系统资源、传感器句柄(如 GPS)或任何在您的 Activity 暂停且用户不需要它们时影响电池寿命的资源。

然而,如 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 相关的工作仍会继续。

此外,使用 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 停止时销毁了进程,系统仍然会在 Bundle(一个键值对的 blob)中保留 View 对象的状态,例如 EditText 微件中的文本,如果用户导航回 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 状态
最低 前台(拥有或即将获得焦点) Resumed
可见(无焦点) Started/Paused
较高 后台(不可见) Stopped
最高 空闲 Destroyed

表 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 重新创建以及如何从 View 系统和 Jetpack Compose 对这些配置更改做出反应的更多信息,请查看处理配置更改页面。

实例状态

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

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

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

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

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

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

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

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

当您的 Activity 开始停止时,系统会调用 onSaveInstanceState() 方法,以便您的 Activity 可以将状态信息保存到实例状态捆绑包中。此方法的默认实现保存有关 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

由于 onCreate() 方法无论系统是创建 Activity 的新实例还是重新创建以前的实例都会调用,因此在尝试读取状态 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.
    }
    // ...
}

除了在 onCreate() 期间恢复状态之外,您还可以选择实现 onRestoreInstanceState(),系统会在 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。这种需求 arises, for instance, when an app needs to move from the current screen to a new one.

根据您的 Activity 是否需要从新 Activity 返回结果,您可以使用 startActivity() 方法或 startActivityForResult() 方法启动新 Activity。在这两种情况下,您都传入一个 Intent 对象。

Intent 对象指定您要启动的确切活动,或描述您要执行的操作类型。系统会为您选择适当的活动,该活动甚至可以来自不同的应用程序。Intent 对象还可以携带少量数据供启动的活动使用。有关 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,它们可以为您执行这些操作。

这就是意图真正有价值的地方。您可以创建一个描述您要执行的操作的意图,系统会从另一个应用程序启动适当的活动。如果有多个活动可以处理该意图,那么用户可以选择使用哪个活动。例如,如果您想让用户发送电子邮件,您可以创建以下意图:

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

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

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 停止运行并进入 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 的转换。