Activity 常见问题总结

2,129 阅读10分钟

Activity 的生命周期

Activity 生命周期

生命周期方法

  • onCreate
    首次创建 Activity 时调用,应该在这里执行必须的初始化工作,例如 setContentView、加载数据、绑定 View 等。
  • onStart
    在 Activity 即将对用户可见之前调用,这里可以做一些 UI 相关的初始化操作,也可以注册 Receiver、开始监听 Bus 事件等。
  • onResume
    在 Activity 即将开始与用户进行交互之前调用。此时 Activity 处于栈顶并接收所有的用户输入,可以在这里初始化相机、开始播放视频等。
  • onPause
    在 Activity 失去焦点时会被调用,此时依然可见,但很有可能会紧接 onStop 然后退出 Activity。此时可以做一些存储数据、停止动画、释放相机之类的工作,但不能太耗时,因为只有当它完成后,新的 Activity 才能继续执行。
  • onStop
    在 Activity 对用户不可见时调用,例如 Activity 退出或被其它 Activity 覆盖,同样这里可以保存数据、做一些稍微重量级的回收工作。
  • onDestroy
    在 Activity 被销毁前调用。当 Activity 结束(调用 finish 方法),或系统为节省空间销毁此 Activity 时会被调用,可以通过 isFinishing 方法区分这两种情况。所有未释放的资源要在这里回收,不需要继续执行的异步操作要停止,防止内存泄漏。
  • onRestart
    在 Activity 从完全不可见状态重新回到前台时调用。

生命周期方法中,有三对是相互对应的,即 onCreate 与 onDestroy、onStart 与 onStop、onPause 与 onResume。一些初始化工作例如初始化相机、初始化播放器、开始播放、注册 Receiver、监听 GPS 坐标变化、监听 Bus 事件等,在哪里开始并没有一定之规,应当根据具体的业务需要在 onCreate / onStart / onResume 中选择最合适的,但一定要在其相对应的生命周期方法中执行相反的操作或者释放资源,才能保证在各种状态都不会出现问题。

典型场景

与上述生命周期方法相对应,Activity 有这些状态: Created / Started / Resumed / Paused / Stopped / Destroyed 。其中 Created / Started 是瞬间状态,因为调用 onCreate 之后立刻会调用 onStart,而 onStart 之后也一定会立即调用 onResume。Resumed / Paused / Stopped 是持久状态,例如 Activity 在前台且获得焦点,此时是 Resumed 状态;可见但未获得焦点,例如被一个 dialog 样式的 Activity 覆盖、多窗口模式下其它窗口获得了焦点,此时就是 Paused 状态;而 Activity 被完全覆盖且未销毁的情况之下,会一直停留在 Stopped 状态。

单个 Activity 的流程

进入:onCreate -> onStart -> onResume
按 Home 键或者被新的 Activity 完全覆盖:onPause -> onStop
退出:onPause -> onStop -> onDestroy

多个 Activity 协作的流程

Activity A 启动了 Activity B(B 不是 dialog 样式):
A.onPause -> B.onCreate -> B.onStart -> B.onResume -> A.onStop
从 Activity B 再退回 Activity A:
B.onPause -> A.onRestart -> A.onStart -> A.onResume -> B.onStop -> B.onDestroy

异常情况下的生命周期

系统配置发生变化

系统配置发生变化时,Activity 会被销毁并重建,因为 Android 系统中的资源都是和系统配置相关的,例如我们的 layout 可能取决于设备是横屏还是竖屏,String 取决于当前系统语言等,重建 Activity 会根据新的系统配置加载合适的资源。
销毁时 Activity 的 onPause、onStop、onDestroy 方法会依次被调用,同时系统会调用 onSaveInstanceState 方法保存当前 Activity 的状态到 Bundle 对象中;重建时系统会调用 onRestoreInstanceState 方法并将之前保存的 Bundle 对象传递给 onCreate 方法和 onRestoreInstanceState 方法。

系统内存不足

当系统内存不足时,低优先级的 Activity 会被系统杀死,优先级顺序从高到低如下:

  1. 前台 Activity:正在和用户交互的 Activity,即 Resumed 状态的 Activity。
  2. 可见 Activity:可见但不能和用户交互的 Activity,即 Paused 状态的 Activity。
  3. 后台 Activity:已经被暂停的 Activity,即 Stopped 状态的 Activity。

当系统内存不足时,会按照由低到高的优先级顺序去杀掉目标 Activity 所在进程,并在后续通过 onSaveInstanceState 和 onRestoreInstanceState 来存储和恢复数据。没有运行四大组件的进程优先级最低,很容易被系统杀死,因此后台工作不应当脱离四大组件单独运行,可以放到 Service 中。

onSaveInstanceState 和 onRestoreInstanceState

Android P 之前 onSaveInstanceState 方法会在 onStop 之前被调用,有可能在 onPause 之前也有可能在 onPause 之后;Android P 及之后的的版本中 onSaveInstanceState 方法会在 onStop 之后被调用。
onRestoreInstanceState 方法会在 onStart 方法之后调用,其传入的 Bundle 对象一定不是 null,恢复的工作也可以放到 onCreate 方法中。 onSaveInstanceState 方法和 onRestoreInstanceState 方法的默认实现会保存和恢复当前界面中所有 View 的状态,不需要做特殊处理。
onSaveInstanceState 方法会在 Activity 需要销毁重建,或者被切换到后台时被调用,例如切换横竖屏、锁屏、被新的 Activity 完全遮住、按 Home 键等。但这两个方法不一定是成对的被调用的,onRestoreInstanceState 被调用的前提是,Activity 确实被系统销毁了并且在重建,大部分情况下调用 onSaveInstanceState 并不意味着 Activity 真的会被销毁,只是为了保险而已。

常见问题

onPause 和 onStop 有什么区别?

这是个很古老的问题。二者最明显的区别是是否可见,如果 Activity 失去焦点就会调用 onPause,但只有 Activity 完全不可见时才会调用 onStop。只调用 onPause 不调用 onStop 情景可以举下面两个例子:

  • 当前 Activity 被另一个 dialog 样式的 Activity 覆盖,此时不可与用户交互但仍然可见。
  • 多窗口模式下两个 App 并列显示,但焦点被另一个 App 抢走。

应该在 onPause 还是 onStop 中保存数据?

这个问题有些麻烦,Android 官方文档其实是存在自相矛盾的,我们来分析一下:
Understand the Activity Lifecycle 中有这样一段话:

onPause() execution is very brief, and does not necessarily afford enough time to perform save operations. For this reason, you should not use onPause() to save application or user data, make network calls, or execute database transactions; such work may not complete before the method completes. Instead, you should perform heavy-load shutdown operations during onStop().

大意是讲,onPause 方法应该快速结束,不应该在其中进行耗时操作,例如保存用户数据、发起网络请求、执行数据库事物等,这些重量级的工作应该放到 onStop 当中。
然而 Activity 类的文档中又有相反的说法:

onPause() is where you deal with the user pausing active interaction with the activity. Any changes made by the user should at this point be committed (usually to the ContentProvider holding the data). In this state the activity is still visible on screen. Note the "Killable" column in the above table -- for those methods that are marked as being killable, after that method returns the process hosting the activity may be killed by the system at any time without another line of its code being executed. Because of this, you should use the onPause() method to write any persistent data (such as user edits) to storage.

就是说 Activity 在执行完 onPause 方法后会进入”Killable“状态,系统有可能在不执行 onStop 的情况下直接杀掉进程,如果没有在 onPause 中保存数据,就没有机会了。然而 Android 3.0 之后有了细微的变化,系统可以保证杀掉进程时 onStop 也会被调用:

Be aware that these semantics will change slightly between applications targeting platforms starting with Build.VERSION_CODES.HONEYCOMB vs. those targeting prior platforms. Starting with Honeycomb, an application is not in the killable state until its onStop() has returned. This impacts when onSaveInstanceState(android.os.Bundle) may be called (it may be safely called after onPause()) and allows an application to safely wait until onStop() to save persistent state.

综合上面的观点,如果不考虑兼容 3.0 之前的系统(市面已经很少了),在 onPause 或者 onStop 中保存数据其实都可以的,都能够得到保证。但是 onPause 方法不适合执行太耗时的操作,因为无论是开启新的 Activity 还是退出当前 Activity,都要等到 onPause 方法执行完成后才会看到新的界面,应当尽量为其减轻负担,所以除非一定要在 onPause 中完成的部分,保存数据应当放到 onStop 中。
当然这不是完全绝对的,也要结合具体业务需求。举个例子,比如有两个 Activity A 和 B,用户可以在 A 中设置参数,并且这个设置项要保存到 Preference 中,而 B 需要从 Preference 中读取这个设置项并显示出来。如果 A 在 onStop 方法中保存数据,当用户从 A 启动 B 时就会出问题:由于 A 的 onStop 方法调用其实在 B 初始化之后,此时 B 是无法获取到这个最新值的,这种情况 A 只能在 onPause 方法中保存数据。
另外,在 Activity 文档中 Saving Persistent State 这一节,官方建议使用“edit in place”的方式来保存数据,这种策略和 AndroidStudio 保存文件的策略相似,即数据发生改变后要随时保存,这样哪怕发生了 Crash 或者手机断电之类的极端情况,也可以保证数据不会丢失,比在 onPause 或者 onStop 中保存数据要更靠谱。

onSaveInstanceState 和 onPause、onStop 等有什么区别和联系?

onSaveInstanceState 和 onRestoreInstanceState 并不是 Activity 的生命周期方法,我们在上面那张生命周期流程图中也没有看到它们。onPause 和 onStop 是 Activity 停止或退出时会调用的方法,因此我们一般会在其中做保存数据的操作,也就是将数据持久化到磁盘。onSaveInstanceState 方法只有系统需要重建 Activity 时,例如退到后台、系统设置发生变化、系统内存不足杀掉 Activity 时才会被调用,如果是正常的 finish 流程则不会被调用。onSaveInstanceState 虽然也是保存数据,但它的目标并不是将数据持久化,而是将一些和 UI、当前状态相关的变量等交给系统来保存,如果这个 Activity 还需要再恢复,就可以用这些数据还原现场。
总结一下,调用场景不同,调用的目的也不同,所以说它们并没有什么必然的联系。

Activity 的启动模式 - launchMode

standard(默认)

标准启动模式,每个 Intent 重新生成一个实例。

singleTop

复用栈顶模式,如果已有相同 Activity 实例且位于栈顶,则不会重新生成实例,只会调用栈顶 Activity 的 onNewIntent() 方法;如果已有相同 Activity 且不在栈顶,还是会生成新实例,同 standard 模式。

singleTask

首先根据指定的 taskAffinity 查找任务,如果存在就在已有任务中启动,否则启动新的任务;如果在已有任务中启动,会在任务中查找当前 Activity 的实例,找到就将其上面的所有 Activity 结束并调用该实例的 onNewIntent() 方法,否则创建新的实例。

singleInstance

单独占用一个任务,再次启动时复用已有任务和实例。该 Activity 启动的 Activity 会运行在其它任务中,和 singleTask 情况类似。

参考资料

Understand the Activity Lifecycle  |  Android Developers
Activity  |  Android Developers
任务和返回栈  |  Android Developers
《Android 开发艺术探索》第1章
Android中Activity四种启动模式和taskAffinity属性详解 - 张纪刚的博客 - CSDN博客
解开Android应用程序组件Activity的"singleTask"之谜 - 老罗的Android之旅 - CSDN博客