activity 生命周期

当用户浏览、退出和返回到您的应用时,您应用中的 Activity 实例会在其生命周期的不同状态间转换。Activity 类提供了许多回调,这些回调会让 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 生命周期方法。不过,应将实现依赖组件操作的代码(而不是 activity 生命周期方法)放置在组件中。为了实现这一点,您需要将依赖组件设为生命周期感知型组件。如需了解如何使依赖组件具有生命周期感知能力,请参阅 使用生命周期感知型组件处理生命周期

onCreate()

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

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

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

以下 onCreate() 方法示例展示了 activity 的基本设置,例如声明界面(在 XML 布局文件中定义)、定义成员变量,以及配置某些界面。在此示例中,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 不会保持“已创建”状态。onCreate() 方法执行完毕后,activity 会进入“已开始”状态,系统会相继调用 onStart()onResume() 方法。

onStart()

当 activity 进入“已开始”状态时,系统会调用 onStart()。当应用准备 activity 进入前台并实现互动时,此调用会向用户显示该 activity。例如,此方法用于初始化维护界面的代码。

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

onStart() 方法会快速完成,并且与“已创建”状态一样,activity 不会保持“已开始”状态。一旦此回调完成,activity 就会进入“已恢复”状态,系统会调用 onResume() 方法。

onResume()

当 activity 进入“已恢复”状态时,会进入前台,然后系统会调用 onResume() 回调。这是应用与用户互动的状态。应用会保持这种状态,直到发生某些事件(例如设备收到来电、用户转到其他 activity 或设备屏幕关闭)时将焦点从应用移开。

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

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

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

以下示例展示了生命周期感知型组件会在收到 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 也可能完全可见。例如,当应用处于多窗口模式且用户点按不包含 activity 的窗口时,activity 将进入“已暂停”状态。

如果您希望摄像头仅在应用恢复(在前台可见且处于活动状态)时处于活动状态,请在前面演示的 ON_RESUME 事件后初始化摄像头。如果您想在 activity 处于“已暂停”状态但可见时(例如在多窗口模式下)使相机保持活动状态,请在发生 ON_START 事件后初始化相机。

不过,如果让摄像头在 activity 处于“已暂停”状态时处于活动状态,可能会拒绝在多窗口模式下向另一个“已恢复”的应用访问摄像头。有时,您必须在 activity 处于“已暂停”状态时让相机保持活动状态,但这样做实际上可能会降低整体用户体验。

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

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

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

onPause()

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

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

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

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

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

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

下例中的 LifecycleObserver 会响应 ON_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 离开“已暂停”状态。相反,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() 的实现,它将草稿笔记内容保存到持久性存储空间中:

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 进入“已停止”状态时,Activity 对象将驻留在内存中:它会保留所有状态和成员信息,但不会附加到窗口管理器。activity 恢复后会重新调用这些信息。

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

注意:Activity 停止后,如果系统需要恢复内存,可能会销毁包含该 Activity 的进程。即使系统在 activity 停止时销毁了进程,系统仍会在 Bundle(键值对 blob)中保留 View 对象(例如 EditText widget 中的文本)的状态,并在用户返回到 activity 时恢复这些对象。如需详细了解如何恢复用户返回的 activity,请参阅有关保存和恢复状态的部分。

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

onDestroy()

销毁 Ativity 之前,系统会先调用 onDestroy()。系统会出于以下两种原因之一调用此回调:

  1. activity 即将结束,这是由于用户完全关闭 activity 或由于对 activity 调用了 finish()
  2. 由于配置变更(如设备旋转或进入多窗口模式),系统会暂时销毁 activity。

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

请使用 ViewModel 对象来包含 Activity 的相关视图数据,而不是在 Activity 中添加逻辑来确定其被销毁的原因。如果由于配置更改而重新创建了 Activity,则 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 onSaveInstanceState() 和/或本地存储空间来保留用户的瞬时界面状态。如需详细了解与系统行为相比的用户预期,以及如何在系统发起的 activity 终止和进程终止后妥善保留复杂的界面状态数据,请参阅 保存界面状态

本部分概述了实例状态的定义,以及如何实现 onSaveInstance() 方法,该方法是对 activity 本身的回调。如果您的界面数据是轻量级,那么您可以单独使用 onSaveInstance() 在配置更改和系统发起的进程终止后保留界面状态。但是,由于 onSaveInstance() 会产生序列化/反序列化费用,因此在大多数情况下,您可以同时使用 ViewModelonSaveInstance(),如 保存界面状态中所述。

注意 :如需详细了解配置变更、如何根据需要限制 activity 重新创建,以及如何通过 View 系统和 Jetpack Compose 响应这些配置变更,请参阅处理配置变更页面。

实例状态

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

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

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

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

这样,如果您的 Activity 实例被销毁并重新创建,布局状态便会恢复为其先前的状态,且您无需编写代码。但是,您的 Activity 可能包含您要恢复的更多状态信息,例如追踪用户在 Activity 中的进程的成员变量。

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

Bundle 对象并不适合保留大量数据,因为它需要在主线程上进行序列化,并且会占用系统进程内存。如需保留极少量的数据,请结合使用永久性本地存储空间、onSaveInstanceState() 方法和 ViewModel 类来保留数据,如 保存界面状态中所述。

使用 onSaveInstanceState() 保存简单轻量的界面状态

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

如需保存 activity 的其他实例状态信息,请替换 onSaveInstanceState(),并将键值对添加到在 activity 意外销毁时保存的 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);
}

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

如需保存持久性数据(例如用户偏好设置或数据库的数据),请在 activity 位于前台时抓住适当的机会。如果没有这样的时机,请在使用 onStop() 方法期间保存持久性数据。

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

重建先前被销毁的 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.
    }
    // ...
}

您可以选择实现系统在 onStart() 方法之后调用的 onRestoreInstanceState(),而不是在 onCreate() 期间恢复状态。仅当存在要恢复的已保存状态时,系统才会调用 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。如果有多个 Activity 可以处理 intent,用户可以选择要使用哪一个。例如,如果您想允许用户发送电子邮件,可以创建以下 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 停止运行并进入“已暂停”或“已停止”状态,同时创建另一个 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 的信息转换。