重学 Android - Activity 上--生命周期
最近在看任玉刚的《Android艺术探索 》,准备边看边做笔记。
第一章自然是 Activity
Activity 生命周期

1.正常情况下的生命周期
| 阶段 | 定义 | 备注 |
|---|---|---|
| onCreate | Activity 正在创建 | 加载布局资源,不可进行耗时操作 |
| onRestart | Activity从不可见到可见,比如按Home键回到桌面又返回 | 无 |
| onStart | Activity可见,但不能操作 | 可以重新开始 onPause中暂停的动画等效果 |
| onResume | Activity可见,可交互 | 无 |
| onPause | Activity 正在停止,开始跳转,不可交互,执行完后,新Activity的onResume才会执行 | 界面动画停止,数据存储,不能进行耗时操作 |
| onStop | Activity即将停止,Activity 设置透明主题不会调用 | 可以做稍重一点的回收工作 |
| onDestroy | Activity已销毁 | 释放资源,回收工作 |
2.异常情况下的生命周期

情况1: 资源相关的系统配置发生改变导致 Activity 被杀死并重新创建
比如说当前屏幕从竖屏切换到横屏,在未特殊处理的情况下,Activty 会被销毁并重新创建 Activity,并且会调用 onSaveInstanceState ,保存当前 Activity 状态,并且把保存的状态传递给重新创建 Activity 时调用的onCreate 和 onRestoreInstanceState 方法。同时需要知道的是,在异常销毁的情况下,系统会默认保存销毁前的状态,并在重建时恢复状态。例如,当前布局,布局中 View 的数据等等。具体怎么恢复需要查看各个 View 的源码,它们的都有各自的 onSaveInstanceState 和 onRestoreInstanceState 方法。
接着来实际测试一下 EditText ,会不会自动保存恢复,在这之前先看一下 TextVIew (EditText 继承自 TextView)的 onSaveInstanceState 和 onRestoreInstanceState 源码:
1 | thumbnail: https://images.unsplash.com/photo-1563952565009-c5afd2643dd6?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60 |
以上代码可以看到,在 onSaveInstanceState 中将控件的文本以及样式保存了下来,并且在 onRestoreInstance 中恢复。
再来上实操一下
1 | class AActivity : BaseActivity() { |
1 |
|
咦?怎么没有恢复?
1 | AActivity.onCreate |
从生命周期打印日志可以看出 activty 中手动保存的数据都得到了正确恢复,但是 EditText 的数据并没有得到恢复
经过一番搜索(面向 google),「Android View onSaveInstanceState not called」提到,需要给 View 设置 id ,布局文件或者代码中都行,系统才会正确的找到 View ,并做保存恢复的处理,这是该书中未提到的。
还有一点,我注意到书中强调只有异常销毁情况才会调用 onSaveInstanceState,但经测试,从A跳转到B,或者返回桌面都会调用 onSaveInstanceState 方法,但返回 A 后不会调用 onRestoreInstanceState。再者,即使手动杀死 App ,重新打开 onCreate 也不能恢复数据。
猜测应该是因为 Activity 失去焦点后都用被系统杀掉的可能,所以在某个版本的 Activity 或者继承类中添加了该特性,如「Android面试题-onSaveInstanceState源码内核分析」中提到的。
Android P 版本以后 onSaveInstanceState 在 onStop 之后调用,之前的版本在 onStop 之前调用。
基于 2020-04-18 当前最新编译环境, API 22、27、29 的虚拟机均有调用
情况2:系统资源不足导致优先级低的 Activity 被销毁
这种情况不太好模拟,但数据保存和重建的过程是一致的。而 Activity 的优先级从高到低是这样的:
| 优先级 | 描述 | 周期 |
|---|---|---|
| 高 | 前台 Activity 可见,可交互 | onResume |
| 中 | Activity 可见,不可交互 | 弹出 Dialog |
| 低 | 后台 Activity不可见,不可交互 | onStop |
当系统资源紧缺时,系统会按照优先级低到高的顺序杀死目标 Activity 所在进程,并且会调用 onSaveInstanceState 进行保存数据以及重建时调用 onRestoreInstanceState 恢复数据。
并且一个进程中如果没有四大组件在运行,这个进程会优先被杀死,所以如果有后台工作要进行可以用 Service 放在后台,保证较高优先级。
上面提到,当前系统配置发生改变时(比如旋转屏幕),Activity 会销毁重建。这时可以通过在 AndroidManifest.xml 下的 activity 标签设置 configChanges = “orientation|screenSize”,使得旋转屏幕时不销毁重 Activity,并且旋转后会调用 Activity#onConfigurationChanged 方法,而不会调用其他生命周期方法。除了 orientation 还有一下系统配置变化时会引起这种情况:
| 值 | 描述 |
|---|---|
| “density” | 显示密度发生变更 — 用户可能已指定不同的显示比例,或者有不同的显示现处于活跃状态。此项为 API 级别 24 中的新增配置。 |
| “fontScale” | 字体缩放系数发生变更 — 用户已选择新的全局字号。 |
| “keyboard” | 键盘类型发生变更 — 例如,用户插入外置键盘。 |
| “keyboardHidden” | 键盘无障碍功能发生变更 — 例如,用户显示硬键盘。 |
| “layoutDirection” | 布局方向发生变更 — 例如,自从左至右 (LTR) 更改为从右至左 (RTL)。 此项为 API 级别 17 中的新增配置。 |
| “locale” | 语言区域发生变更 — 用户已为文本选择新的显示语言。 |
| “mcc” | IMSI 移动设备国家/地区代码 (MCC) 发生变更 — 检测到 SIM 并更新 MCC。 |
| “mnc” | IMSI 移动设备网络代码 (MNC) 发生变更 — 检测到 SIM 并更新 MNC。 |
| “navigation” | 导航类型(轨迹球/方向键)发生变更。(这种情况通常不会发生。) |
| “orientation” | 屏幕方向发生变更 — 用户旋转设备。 请注意:如果应用面向 Android 3.2(API 级别 13)或更高版本的系统,则还应声明 “screenSize” 配置,因为当设备在横向与纵向之间切换时,该配置也会发生变更。 |
| “screenLayout” | 屏幕布局发生变更 — 不同的显示现可能处于活跃状态。 |
| “screenSize” | 当前可用屏幕尺寸发生变更。该值表示当前可用尺寸相对于当前纵横比的变更,当用户在横向与纵向之间切换时,它便会发生变更。此项为 API 级别 13 中的新增配置。 |
| “smallestScreenSize” | 物理屏幕尺寸发生变更。 该值表示与方向无关的尺寸变更,因此它只有在实际物理屏幕尺寸发生变更(如切换到外部显示器)时才会变化。对此配置所作变更对应 smallestWidth 配置的变化。 此项为 API 级别 13 中的新增配置。 |
| “touchscreen” | 触摸屏发生变更。(这种情况通常不会发生。) |
| “uiMode” | 界面模式发生变更 — 用户已将设备置于桌面或车载基座,或者夜间模式发生变更。如需了解有关不同界面模式的更多信息,请参阅 UiModeManager。 此项为 API 级别 8 中的新增配置。 |
重学 Android - Activity 上--生命周期