Android 学习之 Activity 生命周期

当用户浏览、退出和返回到您的应用时,应用中的 Activity 实例会在其生命周期的不同状态间转换。Activity 类会提供许多回调,这些回调会让 Activity 知晓某个状态已经更改:系统正在创建、停止或恢复某个 Activity,或者正在销毁该 Activity 所在的进程。

Activity 生命周期概念

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

下图是对此范例的直观展现。

当用户开始离开 Activity 时,系统会调用方法来销毁该 Activity。在某些情况下,此销毁只是部分销毁;Activity 仍然驻留在内存中(例如当用户切换至另一应用时),并且仍然可以返回到前台。如果用户返回到该 Activity,Activity 会从用户离开时的位置继续运行。除了少数例外,应用在后台运行时会受到限制,无法启动 Activity

系统终止给定进程及其中 Activity 的可能性取决于当时 Activity 的状态。Activity 状态和从内存中弹出 会更详细地介绍状态与弹出漏洞之间的关系。

根据 Activity 的复杂程度,您可能不需要实现所有生命周期方法。但是,请务必了解每个方法,并实现能够确保应用按用户预期方式运行的方法,这非常重要。

在下一部分中,本文档将详细介绍用于处理状态间转换的回调。

生命周期回调

本部分介绍 Activity 生命周期中所用回调方法的相关概念及实现信息。

某些操作(例如调用 setContentView())属于 Activity 生命周期方法本身。不过,用于实现依赖组件操作的代码应放在组件本身内。为此,您必须使依赖组件具有生命周期感知能力。请参阅使用生命周期感知型组件处理生命周期,了解如何让您的依赖组件获得生命周期感知能力。

onCreate()

您必须实现此回调,它会在系统首次创建 Activity 时触发。Activity 会在创建后进入“已创建”状态。在 onCreate() 方法中,您需执行基本应用启动逻辑,该逻辑在 Activity 的整个生命周期中只应发生一次。例如,onCreate() 的实现可能会将数据绑定到列表,将 Activity 与 ViewModel 相关联,并实例化某些类作用域变量。此方法会接收 savedInstanceState 参数,后者是包含 Activity 先前保存状态的 Bundle 对象。如果 Activity 此前未曾存在,Bundle 对象的值为 null。

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

onCreate() 方法的以下示例显示执行 Activity 某些基本设置的一些代码,例如声明界面(在 XML 布局文件中定义)、定义成员变量,以及配置某些界面。在本示例中,系统通过将文件的资源 ID R.layout.main_activity 传递给 setContentView() 来指定 XML 布局文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
TextView textView;

// some transient state for the activity instance
String gameState;

@Override
public void onCreate(Bundle savedInstanceState) {
// call the super class onCreate to complete the creation of activity like
// the view hierarchy
super.onCreate(savedInstanceState);

// recovering the instance state
if (savedInstanceState != null) {
gameState = savedInstanceState.getString(GAME_STATE_KEY);
}

// set the user interface layout for this activity
// the layout file is defined in the project res/layout/main_activity.xml file
setContentView(R.layout.main_activity);

// initialize member TextView so we can manipulate it later
textView = (TextView) findViewById(R.id.text_view);
}

// This callback is called only when there is a saved instance that is previously saved by using
// onSaveInstanceState(). We restore some state in onCreate(), while we can optionally restore
// other state 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 may 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() 回调。

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 事件时访问相机:

1
2
3
4
5
6
7
8
9
10
11
12
13
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 仍然可见)。使用 onPause() 方法暂停或调整当 Activity 处于“已暂停”状态时不应继续(或应有节制地继续)的操作,以及您希望很快恢复的操作。Activity 进入此状态的原因有很多。例如:

  • onResume() 部分所述,某个事件会中断应用执行。这是最常见的情况。
  • 在 Android 7.0(API 级别 24)或更高版本中,有多个应用在多窗口模式下运行。无论何时,都只有一个应用(窗口)可以拥有焦点,因此系统会暂停所有其他应用。
  • 有新的半透明 Activity(例如对话框)处于开启状态。只要 Activity 仍然部分可见但并未处于焦点之中,它便会一直暂停。

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

您还可以使用 onPause() 方法释放系统资源、传感器(例如 GPS)手柄,或当您的 Activity 暂停且用户不需要它们时仍然可能影响电池续航时间的任何资源。然而,正如上文的 onResume() 部分所述,如果处于多窗口模式,“已暂停”的 Activity 仍完全可见。因此,您应该考虑使用 onStop() 而非 onPause() 来完全释放或调整与界面相关的资源和操作,以便更好地支持多窗口模式。

响应 ON_PAUSE 事件的以下 LifecycleObserver 示例与上述 ON_RESUME 事件示例相对应,会释放在收到 ON_RESUME 事件后初始化的相机:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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() 期间执行的合适操作,请参阅 onStop()。如需详细了解如何保存数据,请参阅保存和恢复 Activity 状态

onPause() 方法的完成并不意味着 Activity 离开“已暂停”状态。相反,Activity 会保持此状态,直到其恢复或变成对用户完全不可见。如果 Activity 恢复,系统将再次调用 onResume() 回调。如果 Activity 从“已暂停”状态返回“已恢复”状态,系统会让 Activity 实例继续驻留在内存中,并会在系统调用 onResume() 时重新调用该实例。在这种情况下,您无需重新初始化在任何回调方法导致 Activity 进入“已恢复”状态期间创建的组件。如果 Activity 变为完全不可见,系统会调用 onStop()。下一部分将介绍 onStop() 回调。

onStop()

如果您的 Activity 不再对用户可见,说明其已进入“已停止”状态,因此系统将调用 onStop() 回调。例如,当新启动的 Activity 覆盖整个屏幕时,可能会发生这种情况。如果 Activity 已结束运行并即将终止,系统还可以调用 onStop()

当 Activity 进入已停止状态时,与 Activity 生命周期相关联的所有生命周期感知型组件都将收到 ON_STOP 事件。这时,生命周期组件可以停止在组件未显示在屏幕上时无需运行的任何功能。

onStop() 方法中,应用应释放或调整在应用对用户不可见时的无用资源。例如,应用可以暂停动画效果,或从精确位置更新切换到粗略位置更新。使用 onStop() 而非 onPause() 可确保与界面相关的工作继续进行,即使用户在多窗口模式下查看您的 Activity 也能如此。

您还应使用 onStop() 执行 CPU 相对密集的关闭操作。例如,如果您无法找到更合适的时机来将信息保存到数据库,可以在 onStop() 期间执行此操作。以下示例展示了 onStop() 的实现,它将草稿笔记内容保存到持久性存储空间中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@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 会重新调用这些信息。您无需重新初始化在任何回调方法导致 Activity 进入“已恢复”状态期间创建的组件。系统还会追踪布局中每个 View 对象的当前状态,如果用户在 EditText 微件中输入文本,系统将保留文本内容,因此您无需保存和恢复文本。

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

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

onDestroy()

销毁 Activity 之前,系统会先调用 onDestroy()。系统调用此回调的原因如下:

  1. Activity 即将结束(由于用户彻底关闭 Activity 或由于系统为 Activity 调用 finish()),或者
  2. 由于配置变更(例如设备旋转或多窗口模式),系统暂时销毁 Activity

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

您应使用 ViewModel 对象来包含 Activity 的相关视图数据,而不是在您的 Activity 中加入逻辑来确定 Activity 被销毁的原因。如果因配置变更而重新创建 Activity,ViewModel 不必执行任何操作,因为系统将保留 ViewModel 并将其提供给下一个 Activity 实例。如果不重新创建 Activity,ViewModel 将调用 onCleared() 方法,以便在 Activity 被销毁前清除所需的任何数据。

您可以使用 isFinishing() 方法区分这两种情况。

如果 Activity 即将结束,onDestroy() 是 Activity 收到的最后一个生命周期回调。如果由于配置变更而调用 onDestroy(),系统会立即新建 Activity 实例,然后在新配置中为新实例调用 onCreate()

onDestroy() 回调应释放先前的回调(例如 onStop())尚未释放的所有资源。

UIViewController、Activity生命周期异同

Activity 生命周期,一共6个回调方法,可以分为3组:

  • onCreate(),页面创建。
  • onStart(),显示页面,即将获取焦点。
  • onResume(),已获取焦点,可以和用户交互。
  • onPause(),页面即将消失,即将失去焦点。
  • onStop(),页面已消失,失去焦点。
  • onDestroy(),页面销毁。

UIViewController 的生命周期和 Activity 一样,也有 6 个方法,也可以分为三组:

  • viewDidLoad,页面创建
  • viewWillAppear,显示页面,即将获取焦点。
  • viewDidAppear,已获取焦点,可以和用户交互。
  • viewWillDisappear,页面即将消失,即将失去焦点。
  • viewDidDisappear,页面已消失,失去焦点。
  • dealloc,页面销毁。

UIViewController 和 Activity 生命周期的不同

如果是 Android,按任务键或者 Home 键,会回调栈顶的 Activity 的生命周期,顺序:onPause() => onStop()。

而 UIViewController 则没有回调,而是回调 AppDelegate,回调 applicationDidEnterBackground,当再点击桌面上App的图标,则是回调 applicationWillEnterForeground。

AppDelegate 类似 Android 中的 Application,App 生命周期中只有一个,是一个单例,applicationDidEnterBackground为App 进入后台,applicationWillEnterForeground为App 进入后台。

这是 2 个平台设计的差异,iOS 的提供了前、后台切换的回调,而 Android 则没有,需要自己去实现。

文档