活动生命周期

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

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

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

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

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

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

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

Activity 生命周期概念

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

图 1 以视觉方式呈现了这种范例。

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

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

如果用户返回到活动,它将从用户离开的地方恢复。除了一些例外情况,应用在后台运行时无法启动活动

系统杀死给定进程(及其中的活动)的可能性取决于活动当时的 state。有关 state 与易于被移除之间关系的更多信息,请参阅有关活动 state 和从内存中移除的部分。

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

生命周期回调

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

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

onCreate()

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

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

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

以下onCreate()方法示例显示了活动的 fundamental 设置,例如声明用户界面(在 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()来使用该布局。有关创建用户界面的更多信息,请参阅用户界面文档。

您的活动不会一直保持在已创建 state。在onCreate()方法执行完毕后,活动进入已启动 state,并且系统会依次快速调用onStart()onResume()方法。

onStart()

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

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

onStart()方法完成得很快,并且与已创建 state 一样,活动不会一直保持在已启动 state。此回调完成后,活动进入已恢复 state,并且系统会调用onResume()方法。

onResume()

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

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

当发生中断性事件时,活动进入已暂停 state,并且系统会调用onPause()回调。

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

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

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

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

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

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

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

onPause()

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

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

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

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

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

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

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

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(键值对的 blob)中,并在用户导航回活动时恢复它们。有关将活动恢复到用户返回到的活动的信息,请参阅有关 保存和恢复状态 的部分。

从停止状态,活动要么返回与用户交互,要么活动完成运行并消失。如果活动返回,系统将调用 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,由android:id属性提供。

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

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

当您的活动开始停止时,系统会调用onSaveInstanceState()方法,以便您的活动可以将状态信息保存到实例状态捆绑包中。此方法的默认实现会保存有关活动视图层次结构状态的瞬态信息,例如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()方法执行。

此生命周期回调序列允许您管理从一个活动到另一个活动的信息转换。