关于 Activity 的生命周期,可以说是每一位 Android 开发者的入门必备知识,也可以说是入了门就几乎不会再翻看的内容。

相信同行们都知道,这是初级开发者面试必问的问题,虽然问这个问题的意义并不大,主要还是面试官要找一个开场过渡一下罢了。

虽说简单,但是如果答不上来,那可不是出师未捷身先死?

先来放一张来自官方文档陈年经典老图:

Activity Lifecycle

下面一一介绍一下生命周期的七个函数调用:

  • onCreate() :当 Activity 第一次创建时会被调用。这是生命周期的第一个方法,在这个方法中,可以做一些初始化工作,比如调用 setContentView() 去加载界面布局资源,初始化 Activity 所需的数据。当然也可借助 onCreate() 方法中的 Bundle 对象来回复异常情况下 Activity 结束时的状态。
  • onRestart() :表示 Activity 正在重新启动。一般情况下,当当前 Activity 从不可见重新变为可见状态时,onRestart() 就会被调用。这种情形一般是用户行为导致的,比如用户按 HOME 键切换到桌面或打开了另一个新的 Activity,接着用户又回到了这个 Actvity
  • onStart() :表示 Activity 正在被启动,即将开始,这时 Activity 已经出现了,但是还没有出现在前台,无法与用户交互。这个时候可以理解为 Activity 已经显示出来,但是我们还看不到。
  • onResume() :表示 Activity 已经可见了,且出现在前台并开始活动。需要和 onStart() 对比,onStart() 的时候 Activity 还在后台, onResume() 的时候 Activity 才显示到前台。
  • onPause() :表示 Activity 正在停止但仍可见,正常情况下,紧接着 onStop() 就会被调用。在特殊情况下,如果这个时候快速地回到当前 Activity,那么 onResume() 就会被调用(极端情况)。onPause() 中不能进行耗时操作,会影响到新 Activity 的显示。因为 onPause() 必须执行完,新的 ActivityonResume() 才会执行。
  • onStop() :表示 Activity 即将停止,不可见,位于后台。可以做稍微重量级的回收工作,同样不能太耗时。
  • onDestory() :表示 Activity 即将销毁,这是 Activity 生命周期的最后一个回调,可以做一些回收工作和最终的资源回收。

另一张来自《Android programming: the big nerd ranch guide》的图我觉得也能够很好地帮助理解:

Activity State

这里说的是 Activity 的三种运行状态:

  • Resumed(活动状态):又叫 Running 状态,这个 Activity 正在屏幕上显示,并且有用户焦点。这个很好理解,就是用户正在操作的那个界面。
  • Paused(暂停状态):这是一个比较不常见的状态。这个 Activity 在屏幕上是可见的,但是并不是在屏幕最前端的那个 Activity。比如有另一个非全屏的或者透明的 Activity 是 Resumed 状态,没有完全遮盖这个 Activity
  • Stopped(停止状态):当 Activity 完全不可见时,此时 Activity 还在后台运行,仍然在内存中保留 Activity 的状态,并不是完全销毁。这个也很好理解,当跳转的另外一个界面,之前的界面还在后台,按回退按钮还会恢复原来的状态,大部分软件在打开的时候,直接按 HOME 键,并不会关闭它,此时的 Activity 就是 Stopped 状态。

当然,有些文章也会把它描述成五种状态,并不影响理解:

Activity 的五种状态

只不过是把启动和销毁两种状态加进来罢了,实际上讨论这两种状态的意义并不大,可以作为参考。

综合之后,我们可以参考这张图:

Activity State and Lifecycle

接下来是关于生命周期在一些情况下的表现。

  1. 正常情况下从一个 Activity 启动另一个 Activity,两个 Activity 的生命周期如何交替?
正常启动新的 Activity

可能有些人不太理解为什么要先执行原 ActivityonPause() 方法再启动新 Activity 的创建,可以假设这么一种情况,比如你通过一个按钮来启动一个 Activity,快速双击这个按钮,如果我们等新的 Activity 创建完成了再执行原 ActivityonPause(),那么就可能会创建两个相同的 Activity 在栈中,所以必须要先执行原 ActivityonPause(),使其进入 Paused 状态,让它无法与用户交互。

  1. 点击 OVERVIEW 键调出最近任务后会执行哪些生命周期函数?

很多人不知道 OVERVIEW 键是哪个,因为这种说法也很少有,其实就是 Android 手机三大金刚键除了 HOME 和 BACK 剩下的那个,默认情况下该键用于调出最近任务:

Overview

但实际上该键经历了很多变更,比如在早期该键其实用于呼出菜单,直至近年来才统一为默认调出最近任务,但一些老用户(比如我)还是会将其修改为呼出菜单,而将长按 HOME 键设置为调出最近任务。

所以这里的意思是调出上述最近任务界面后,原来的 Activity 会执行哪些生命周期函数:

呼出最近任务界面

一般会执行 onPause()onStop() 两个方法,但不绝对,也有部分机型并不会执行生命周期的任何方法。

  1. 锁屏后当前 Activity 会执行哪些生命周期函数?

和用户点击 HOME 键回到桌面或者调出最近任务相似,锁屏后也会执行 onPause() 以及 onStop() 两个方法。

  1. 什么时候会仅执行 onPause() 而不执行 onStop()

上面介绍 Activity 运行状态时也有提到,执行 onPause() 而不执行 onStop(),即 Activity 进入了 Paused 状态,此时比如有另一个非全屏的或者透明的 Activity 是 Resumed 状态,即在前台和用户交互,原来这个可见但不可交互的 Activity 就是处于 Paused 状态,不会执行到 onStop()

Dialog 主题的 Activity

如『Android 使用 Dialog 样式的 Activity』一文提到的把 Activity 的主题设置为 Dialog 样式。

  1. 弹出 AlertDialogActivity 会执行哪些生命周期函数?
弹出 AlertDialog

并不会执行 Activity 的任何生命周期函数。

其实这里有个很大的误区,许多刚入门的开发者或许会在一些博客中看到一些博主笼统地概括:弹出弹窗后 Activity 就会进入 Paused 状态。

事实上并不是,而只有上一点提到的 Dialog 主题的 Activity 才会使原 Activity 进入 Paused 状态,而普通的 AlertDialog 并不会使原 Activity 进入 Paused 状态。

  1. 长按 POWER 键会执行哪些生命周期函数?

长按 POWER 键一般会弹出一个关机或重启的选项界面,不同的 ROM 其实界面也不尽相同,比如原生 Android 9.0 中是从侧边弹出一个选项菜单,而国内的大部分 ROM 会弹出一个全屏的界面让用户选择。

||
|--|--|

那么这两种情况是否会触发不一样的生命周期函数呢?

答案是否定的,长按 POWER 键并不会执行任何生命周期函数,无论你是半遮挡还是全遮挡。

  1. 下拉状态栏会执行哪些生命周期函数?

下拉通知栏不会执行任何生命周期函数。

  1. 切换屏幕方向时会执行哪些生命周期函数?

在横竖屏切换的过程中,会发生 Activity 被销毁并重建的过程。

虽然可以简单粗暴地描述成 Activity 经历了一次完整的生命周期后再次重建,但实际上要复杂些。

了解这种情况下的生命周期时,首先应该了解这两个回调:onSaveInstanceState()onRestoreInstanceState()

Activity 由于异常情况下终止时,系统会调用 onSaveInstanceState() 来保存当前 Activity 的状态。这个方法的调用是在 onDestroy() 之前,它和onPause() 没有既定的时序关系,该方法只在 Activity 被异常终止的情况下调用。

当异常终止的 Activity 被重建以后,系统会调用 onRestoreInstanceState(),并且把 Activity 销毁时 onSaveInstanceState() 方法所保存的 Bundle 对象参数同时传递给 onRestoreInstanceState()onCreate() 方法,你可以很明显的看到,onCreate() 方法入参就有一个 Bundle 对象。

因此,可以通过 onRestoreInstanceState() 方法来恢复 Activity 的状态,该方法的调用时机是在 onStart() 之后。

其中 onCreate()onRestoreInstanceState() 方法来恢复 Activity 的状态的区别:onRestoreInstanceState() 回调则表明其中 Bundle 对象非空,不用加非空判断,而 onCreate() 需要非空判断。建议使用 onRestoreInstanceState()

异常终止的 Activity

切换屏幕方向时 Activity 生命周期如下:

切换屏幕方向时经历的生命周期

尽管官方文档中提到 onSaveInstanceState() 会调用在 onStop() 之前,并可能会调用在 onPause() 之前,但是我实际测试中发现,它在 Android 9.0 上也有可能会在 onStop() 之后才调用,我暂时找不到解释的理由,所以对于官方的说法,我暂时做一些修改,onSaveInstanceState() 会在 onDestroy() 之前调用,跟 onPause()onStop() 没有既定的时序关系。

当然官方的说法理论上是比我更有说服力的,所以我希望有经验的开发者们能替我解释一下这个现象,或者我以后搞明白了再来修改,先放一张图:

切换屏幕方向时打印的生命周期日志

可以看到,onSaveInstanceState() 方法打印的日志在 onStop() 之后,所以才会出现了上面的疑问。

跟《第一行代码——Android》的作者郭霖交流过后得到的回复是,onSaveInstanceState() 方法不在 Activity 正常的生命周期之中,所以不同系统版本的表现都有可能不一样,只要知道其作用即可,不要依赖于执行的先后顺序去处理逻辑。

在开发中,常常会禁用屏幕旋转,或者在 Activity 中指定以下属性来避免切换屏幕方向时 Activity 的销毁与重建:

<activity
    ...
    android:configChanges="orientation|screenSize" />

指定该属性后,Activity 则会回调以下的方法:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
}
  1. 分屏模式下会执行哪些生命周期函数?

从 Android N 开始就已经引入了 Split Screen,我们通常叫分屏模式或者多窗口模式,打开最近任务就可以选取任意两个应用进入 Split Screen,通常情况下最先选中的应用会处在上方,后选中处在下方,当然,也有一些 ROM 会采用拖动的策略,且操作焦点一般会在后选中的应用中。

讨论 Split Screen 下的生命周期调用,要分成三种情况,分别是进入 Split Screen 时、处于 Split Screen 时、退出 Split Screen 时。

进入 Split Screen 时,即选择应用进入分屏模式,该情况又分为两种小情况,即先选中的应用和后选中的应用。

先来看进入 Split Screen 时先选中的应用,这时该应用会处于屏幕上半部分,且未获得操作焦点:

作为首个选中的应用进入 Split Screen

进入 Split Screen 时 Activity 同样会执行销毁及重建的过程,所以生命周期如下:

作为首个选中的应用进入 Split Screen 时的生命周期

这些函数的执行是连贯的,你可能会认为当用户选中了第二个应用时才会执行 onPause(),实际上并不是,因为用户选中第一个应用后马上就失去操作焦点,此时操作焦点处于让用户选择第二个应用的 OVERVIEW 中,所以当用户选中第一个应用时该应用就处于 Paused 状态。

再来看进入 Split Screen 时后选中的应用,这时该应用会处于屏幕下半部分,且获得了操作焦点:

作为次个选中的应用进入 Split Screen

进入 Split Screen 时 Activity 同样会执行销毁及重建的过程,所以生命周期如下:

作为次个选中的应用进入 Split Screen 时的生命周期

由于此时该应用的 Activity 获得了操作焦点,所以处于 Resumed 状态。

处于 Split Screen 时的生命周期十分好理解,就是获得焦点与失去焦点的切换过程,但由于此时 Activity 总是可见的,所以,当获得焦点时,Activity 就处于 Resumed 状态,失去焦点时,Activity 就处于 Paused 状态,所以在两个应用中切换焦点,就会分别执行 onPause()onResume()

若在处于 Split Screen 时我们进行一些其他的操作,如点击 HOME 键或 OVERVIEW 键,那么处于屏幕上方的应用则会向上折叠,将屏幕下方的空间扩大,适应用户的操作。

Split Screen 时点击 HOME 键

处于上方的应用假如处于 Resumed 状态,那么此时就会调用 onPause(),如果处于 Paused 状态,则不会调用任何生命周期函数,因为其依然可见,只是失去了操作焦点。

但是你点击上方该应用的区域,也是无法回到 Resumed 状态的,因为 Split Screen 必须要由两个应用构成,点按 HOME 键后系统会认为你准备选取第二个应用来填充下方区域,所以此时你要么退出 Split Screen,要么选择应用,不可以单独让一个应用处于 Split Screen。

Split Screen 时尝试调整单个应用

而处于下方的应用则如平常退到后台一样,如果处于 Paused 状态,则直接执行 onStop(),如果处于 Resumed 状态,则还需执行 onPause()

退出 Split Screen 时也分为两种小情况,即退出 Split Screen 后该应用是否处于界面上。

当退出 Split Screen 时选择该应用在界面上活动,则和平时我们正常使用无差别:

退出 Split Screen 且在前台

与进入 Split Screen 时相似,此时的 Activity 也同样进行了一次销毁重建:

退出 Split Screen 且在前台时的生命周期

这个生命周期中有两个判断,是因为同时也有几种情况,我集中一起解说,第一个条件是判断想要处于前台的 Activity 在 Split Screen 时是否处于 Resumed 状态,即是否获取到用户的操作焦点,比较好理解,如果处于 Resumed 状态则需要先执行 onPause(),否则就不需要再执行;接下来就是一个销毁重建的过程,但是测试中意外发现也会出现两种情况,即一开始处于 Paused 状态的应用 Activity 在重建过后会执行两次 onStart()onResume(),而一开始处于 Resumed 状态的应用 Activity 则不会,这就是第二个条件判断,对于此现象我暂时未找到解释的说法。

当退出 Split Screen 时选择其他应用在界面上活动,此时我们的应用则处于后台:

退出 Split Screen 处在后台

处在后台的应用 Activity 生命周期如下:

退出 Split Screen 处在后台时的生命周期

跟我们正常情况下切换到后台也很相似,但还是要作判断,如果 Activity 处于 Resumed 状态则需先执行 onPause() 再执行 onStop()

但是,这样就结束了么?并不是。

当你把该应用从后台再切回前台的时候就会发现,该 Activity 也同样执行了销毁重建:

退出 Split Screen 处在后台并回到前台时的生命周期

与上面处于前台的 Activity 不同,这里不会根据条件判断是否需要执行两次 onStart()onResume()

  1. 强制退出当前应用会执行哪些生命周期函数?

虽然原生 Android 似乎没有提供强制退出当前应用的方法,但很多国内的 ROM 都定制了该功能,如 MIUI 可以设定长按 BACK 键强制关闭等。

MIUI 长按 BACK 键强制退出

Android 9.0 的测试中发现强制退出当前应用时调用了 onPause(),而后面的函数没有调用,这个问题属于边界问题,实际上 onPause() 也不一定会调用,因为强制退出应用实际上是直接从系统中 Kill 掉应用所在的进程,进程不在了,Activity 也就无法监听到生命周期的变化。

  1. 从最近任务中结束应用会执行哪些生命周期函数?

前面提到呼出最近任务会调用 onPause()onStop(),那么在最近任务中结束应用呢?

在 Android 9.0 的测试中见到,会调用 onDestroy()

  1. 关机时当前应用会执行哪些生命周期函数?

上面提到,长按 POWER 键是不会触发生命周期函数的,那么长按 POWER 键再选择关机呢?

测试中显示会执行 onPause()onStop()onSaveInstanceState(),可以发现没有执行 onDestroy(),这个问题比较偏门,属于边界问题,个人认为也可能会产生多种情况。但基本情况下是如此的,它和我们强制退出当前应用比较相似,关机的时候从系统层将应用杀死,所以不会执行 onDestroy()

  1. 当系统内存不足时,哪些 Activity 可能会被杀死?

当系统内存不足时,优先级低的 Activity 会被杀死,而 Activity 优先级的划分和 Activity 的三种运行状态也是对应的。

  • Resumed 状态的 Activity 优先级最高,它正在和用户交互。
  • Paused 状态的 Activity 优先级稍低,它虽然不能和用户交互,但它依然可见。
  • Stopped 状态的 Activity 优先级最低,它已完全不可见。

当系统内存不足时,会按照上述优先级从低到高去杀死目标 Activity 所在的进程。我们在平常使用手机时,能经常感受到这一现象。这种情况下数组存储和恢复过程和上述情况一致,生命周期情况也一样。

这里仅讨论 Activity 所在进程的优先级,其实该问题可以放大到应用进程中进行讨论,比如和 Service 的优先级比较等。


简单做一个总结,我是这样理解:凡是涉及到当前 Activity 的变动,才会触发生命周期的函数,而涉及到系统级的事件不会导致 Activity 的生命周期变化,需要注意的是,虽然弹出 AlertDialog 依赖于 Activity,但实际上也有系统级的 AlertDialog,所以我把 AlertDialog 归类于系统级事件。

该文大致回顾了 Android 中 Activity 的生命周期,它是每个 Android 开发者的基础知识,一定要掌握,掌握的目的,当然不是为了应付面试,而是为了在开发中更好的运用。

毕竟根基不牢,再高的建筑也会倒塌。

另外,我基于 Activity 的生命周期,开发了一款屏幕监视器——『Screen Watchdog』,可以让你知道是否有人偷看了你的手机,有需要的话可以自取哟。