活动生命周期

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

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

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

  • 如果用户在使用您的应用时接听电话或切换到另一个应用,则会崩溃。
  • 当用户没有积极使用它时,会消耗宝贵的系统资源。
  • 如果用户离开您的应用并在稍后返回,则会丢失用户的进度。
  • 当屏幕在横向和纵向之间旋转时,会崩溃或丢失用户的进度。

本文档详细解释了 Activity 生命周期。文档首先描述生命周期范例。接下来,它解释每个回调:在执行期间内部发生的情况以及您需要在执行期间实现的内容。

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

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

Activity 生命周期概念

为了导航 Activity 生命周期各个阶段之间的转换,`Activity` 类提供了一套核心六个回调:`onCreate()`、`onStart()`、`onResume()`、`onPause()`、`onStop()` 和 `onDestroy()`。当 Activity 进入新状态时,系统会调用这些回调中的每一个。

图 1 显示了此范例的可视化表示。

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

当用户开始离开活动时,系统会调用方法来拆解活动。在某些情况下,活动只是部分拆解并仍然驻留在内存中,例如当用户切换到另一个应用程序时。在这些情况下,活动仍然可以回到前台。

如果用户返回到活动,它将从用户离开的地方继续。除少数例外情况外,应用程序在后台运行时,无法启动活动

系统杀死给定进程及其活动可能性取决于活动当时的狀態。有关状态与易于被逐出的关系的更多信息,请参阅有关活动状态和从内存中逐出的部分。

根据活动的复杂性,您可能不需要实现所有生命周期方法。但是,了解每个方法并实现使您的应用程序按用户预期方式运行的方法非常重要。

生命周期回调

本节提供有关在活动生命周期中使用的回调方法的概念和实现信息。

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

onCreate()

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

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

如果您有一个连接到活动生命周期的生命周期感知组件,它将接收ON_CREATE事件。@OnLifecycleEvent注释的方法将被调用,以便您的生命周期感知组件可以执行其为已创建状态所需的任何设置代码。

以下onCreate()方法示例显示了活动的根本设置,例如声明用户界面(在 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()的替代方法,您可以在活动代码中创建新的View对象,并通过将新的View对象插入ViewGroup来构建视图层次结构。然后,您可以通过将根ViewGroup传递给setContentView()来使用该布局。有关创建用户界面的更多信息,请参阅用户界面文档。

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

onStart()

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

当活动移至已启动状态时,任何与活动生命周期绑定的生命周期感知组件都会接收ON_START事件。

onStart()方法会很快完成,并且与已创建状态一样,活动不会停留在已启动状态。此回调完成后,活动将进入已恢复状态,系统将调用onResume()方法。

onResume()

当活动进入已恢复状态时,它将来到前台,系统会调用onResume()回调。这是应用程序与用户交互的状态。应用程序将保持在此状态,直到发生某些事件使应用程序失去焦点,例如设备接听电话、用户导航到另一个活动或设备屏幕关闭。

当活动移至已恢复状态时,任何与活动生命周期绑定的生命周期感知组件都会接收ON_RESUME事件。这是生命周期组件可以启用任何需要在组件可见且位于前台时运行的功能的位置,例如启动相机预览。

发生中断事件时,活动将进入已暂停状态,系统将调用onPause()回调。

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

这是一个生命周期感知组件在组件接收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事件后初始化相机。但是,在多窗口模式下,即使活动处于已暂停状态,它也可能完全可见。例如,当应用程序处于多窗口模式并且用户点击不包含您的活动的窗口时,您的活动将移至已暂停状态。

如果您希望相机仅在应用程序已恢复(在前台可见且处于活动状态)时才处于活动状态,则在前面演示的ON_RESUME事件后初始化相机。如果您希望在活动已暂停但可见时(例如在多窗口模式下)保持相机处于活动状态,则在ON_START事件后初始化相机。

但是,在您的活动已暂停时让相机保持活动状态可能会拒绝在多窗口模式下向另一个已恢复的应用程序访问相机。有时需要在活动已暂停时保持相机处于活动状态,但如果这样做,实际上可能会降低整体用户体验。

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

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

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

onPause()

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

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

当活动移至已暂停状态时,任何与活动生命周期绑定的生命周期感知组件都会接收ON_PAUSE事件。这是生命周期组件可以停止任何无需在组件不在前台时运行的功能的位置,例如停止相机预览。

使用onPause()方法暂停或调整在Activity处于已暂停状态时无法继续或可能适度继续的操作,并且您预计很快会恢复这些操作。

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

但是,正如有关onResume()的部分所述,如果应用程序处于多窗口模式,已暂停的活动可能仍然完全可见。请考虑使用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() 方法的完成并不意味着活动离开暂停状态。相反,活动将保持在此状态,直到活动恢复或对用户完全不可见为止。如果活动恢复,系统将再次调用 onResume() 回调。

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

onStop()

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

当活动移动到停止状态时,任何与活动生命周期绑定的生命周期感知组件都会接收 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 对象将保留在内存中:它维护所有状态和成员信息,但不附加到窗口管理器。当活动恢复时,它会调回此信息。

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

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

从停止状态开始,活动要么恢复与用户的交互,要么活动运行结束并消失。如果活动恢复,系统将调用 onRestart()。如果 Activity 运行结束,系统将调用 onDestroy()

onDestroy()

onDestroy() 在活动被销毁之前调用。系统出于以下两种原因之一调用此回调

  1. 活动即将结束,这是由于用户完全关闭了活动,或者由于在活动上调用了 finish()
  2. 系统由于配置更改(例如设备旋转或进入多窗口模式)而临时销毁了活动。

当活动移动到销毁状态时,任何与活动生命周期绑定的生命周期感知组件都会接收 ON_DESTROY 事件。这是生命周期组件在 Activity 被销毁之前需要清理任何内容的地方。

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

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

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

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

活动状态和内存弹出

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

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

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

系统从不直接终止活动来释放内存。相反,它会终止活动在其内运行的进程,从而不仅销毁活动,还会销毁进程中运行的所有其他内容。要了解如何在系统启动的进程死亡发生时保留和恢复活动的 UI 状态,请参见有关 保存和恢复状态 的部分。

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

有关进程的更多信息,请参见 进程和线程概述

保存和恢复瞬态 UI 状态

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

同样,如果用户暂时从您的应用程序切换到另一个应用程序,然后稍后返回到您的应用程序,用户也期望 UI 状态保持不变。但是,在用户离开并且您的活动已停止时,系统可能会销毁您的应用程序的进程。

当系统约束销毁活动时,请使用 ViewModel onSaveInstanceState() 和/或本地存储的组合来保留用户的瞬态 UI 状态。要了解用户期望与系统行为相比以及如何在系统启动的活动和进程死亡中最佳地保留复杂 UI 状态数据,请参见 保存 UI 状态

本节概述了什么是实例状态以及如何实现 onSaveInstance() 方法,这是活动本身的回调。如果您的 UI 数据很轻量级,则您可以单独使用 onSaveInstance() 来在配置更改和系统启动的进程死亡中持久保存 UI 状态。但是,由于 onSaveInstance() 会产生序列化/反序列化成本,因此在大多数情况下,您会同时使用 ViewModelonSaveInstance(),如 保存 UI 状态 中所述。

注意:要了解有关配置更改的更多信息,如果需要如何限制 Activity 重新创建,以及如何从 View 系统和 Jetpack Compose 中对这些配置更改做出反应,请查看 处理配置更改 页面。

实例状态

在一些情况下,您的活动会因正常的应用程序行为而被销毁,例如当用户按下“返回”按钮或您的活动通过调用 finish() 方法来发出其自身的销毁信号时。

当用户按下“返回”按钮或活动自行结束导致活动被销毁时,系统和用户对该Activity实例的理解都将永久消失。在这种情况下,用户的预期与系统的行为相符,您无需执行任何额外操作。

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

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

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

注意:为了使 Android 系统能够恢复活动中视图的状态,每个视图都必须具有唯一的 ID,该 ID 由android:id属性提供。

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

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

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

要为您的活动保存其他实例状态信息,请覆盖onSaveInstanceState()并将键值对添加到Bundle对象中,该对象在活动意外销毁时保存。覆盖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);
}

注意:当用户显式关闭活动或在其他情况下调用finish()时,不会调用onSaveInstanceState()

要保存持久性数据(例如用户首选项或数据库数据),请在活动处于前台时抓住合适的时机。如果没有这样的机会,请在onStop()方法期间保存持久性数据。

使用保存的实例状态恢复活动 UI 状态

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

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

以下代码片段显示了如何在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()的超类实现,以便默认实现可以恢复视图层次结构的状态。

在活动之间导航

在应用程序的生命周期中,应用程序可能会多次进入和退出活动,例如当用户点击设备的“返回”按钮或活动启动其他活动时。

本节介绍成功实现活动转换所需了解的主题。这些主题包括从另一个活动启动活动、保存活动状态和恢复活动状态。

从另一个活动启动一个活动

活动通常需要在某些时候启动另一个活动。例如,当应用程序需要从当前屏幕移动到新屏幕时,就会出现这种需求。

根据您的活动是否希望从它即将启动的新活动中获得结果,您可以使用startActivity()方法或startActivityForResult()方法启动新活动。在这两种情况下,您都需要传入一个Intent对象。

Intent对象指定要启动的确切活动,或描述要执行的操作类型。系统会为您选择合适的活动,这甚至可以来自不同的应用程序。Intent对象还可以携带少量数据,供启动的活动使用。有关Intent类的更多信息,请参阅意图和意图过滤器

startActivity()

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

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

Kotlin

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

Java

Intent intent = new Intent(this, SignInActivity.class);
startActivity(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);

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

startActivityForResult()

有时您希望在活动结束时从活动中获得结果。例如,您可以启动一个活动,让用户在一个联系人列表中选择一个人。结束时,它将返回所选的人。为此,您可以调用startActivityForResult(Intent, int)方法,其中整数参数标识调用。

此标识符旨在区分来自同一活动的多次startActivityForResult(Intent, int)调用。它不是全局标识符,并且不会与其他应用程序或活动发生冲突。结果将通过您的onActivityResult(int, int, Intent)方法返回。

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

此外,子活动还可以选择返回一个Intent对象,其中包含它想要的所有其他数据。父活动使用onActivityResult(int, int, Intent)方法以及父活动最初提供的整数标识符来接收信息。

如果子活动由于任何原因(例如崩溃)失败,则父活动将收到结果代码为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));
             }
         }
     }
 }

协调活动

当一个活动启动另一个活动时,它们都会经历生命周期转换。第一个活动停止运行并进入暂停或停止状态,而另一个活动被创建。如果这些活动共享保存到磁盘或其他位置的数据,则务必了解,第一个活动在第二个活动创建之前不会完全停止。相反,启动第二个活动的过程与停止第一个活动的过程重叠。

生命周期回调的顺序是明确定义的,尤其是在两个活动位于同一进程(换句话说,同一应用程序)中,并且一个活动正在启动另一个活动时。以下是活动 A 启动活动 B 时发生的顺序:

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

这一系列生命周期回调让你可以管理从一个活动到另一个活动的信息转换。