活动生命周期

当用户在您的应用中导航、退出并返回时,应用中的 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

系统杀死某个进程及其中的所有活动(包括其关联的活动)的可能性取决于该活动当时的运行状态。有关状态与被弹出内存之间的关系的更多信息,请参阅关于活动状态和从内存中弹出的部分。

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

生命周期回调

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

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

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,由 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() 方法执行。

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