1. Activity 生命周期的种类
- 典型情况下的生命周期:在有用户参与的情况下,Activity 所经历的生命周期过程
- 异常情况下的生命周期:由于内存资源不足 Activity 被系统回收 或者 由于当前设备的 Configuration 发生改变从而导致 Activity 被销毁重建
2. 典型情况下 Activity 生命周期各个方法含义
onCreate()
:- 表示 Activity 正在被创建,这是生命周期的第一个方法
- 在这个方法中可以做一些初始化工作,比如调用
setContentView()
去加载界面布局资源、初始化 Activity 所需数据等
onRestart()
:- 表示 Activity 正在被重新启动
- 一般情况下,当当前 Activity 从不可见新变为可见状态时,
onRestart()
方法就会被调用 - 这种情形一般是用户行为导致的,比如用户按 Home 键切换到桌面或者用户打开了一个新的 Activity,这时当前的 Activity 就会暂停,也就是
onPause()
和onStop()
方法被执行了,接着用户又回到了这个 Activity,此时onRestart()
方法就会被回调
onStart()
:- 表示 Activity 正在被启动,即将开始
- 这时 Activity 已经可见了,但是还没有出现在前台,还无法和用户交互。这个时候其实可以理解为 Activity 已经显示出来了,但用户还看不到
onResume()
:- 表示 Activity 已经可见了,并且出现在前台并开始活动
- 要注意和
onStart()
方法的对比,onStart()
和onResume()
都表示 Activity 已经可见,但是onStart()
的时候 Activity 还在后台,onResume()
的时候 Activity 才显示到前台
onPause()
:- 表示 Activity 正在停止,正常情况下,紧接着
onStop()
就会被调用 - 在特殊情况下,如果这个时候再回到当前 Activity,那么
onResume()
会被调用 - 从方法源码注释可知,此时可以做一些存储数据、停止动画等工作,但是注意不能太耗时,因为这会影响到 Activity 的显示,
onPause()
必须先执行完,新 Activity 的onResume()
才会执行
- 表示 Activity 正在停止,正常情况下,紧接着
onStop()
:- 表示 Activity 即将停止
- 可以做一些稍微重量级的回收工作,同样不能太耗时
onDestroy()
:- 表示 Activity 即将被销毁,这是 Activity 生命周期中的最后一个回调
- 在这里,可以做一些回收工作和最终的资源释放操作,但阿里 Android 开发手册中提到一点:
- 【推荐】:不要在
Activity#onDestroy()
方法内执行释放资源的工作,例如一些工作线程的销毁和停止,因为onDestroy()
方法执行的时机可能较晚。可根据实际需要,在Activity#onPause()/onStop()
方法中结合isFinishing()
方法的判断来执行
- 【推荐】:不要在
3. Activity 生命周期中各个方法的配对情况
- 从整个生命周期来说:
onCreate()
和onDestroy()
是配对的,分别标识着 Activity 的创建和销毁,并且只可能有一次调用 - 从 Activity 是否可见来说:
onStart()
和onStop()
是配对的,随着用户的操作或者设备屏幕的点亮和熄灭,这两个方法可能被调用多次 - 从 Activity 是否在前台来说:
onResume()
和onPause()
是配对的,随着用户操作或者设备屏幕的点亮和熄灭,这两个方法可能被调用多次
4. onStart() 和 onResume()、onPause() 和 onStop() 从描述上来看差不多,那它们之间有什么实质的不同
- 这两个配对的回调分别表示不同的意义,
onStart()
和onStop()
是从 Activity 是否可见这个角度来回调的;onResume()
和onPause()
是从 Activity 是否位于前台这个角度来回调的 - 除了角度的区别,在实际使用中没有其他明显区别
5. Activity 的启动过程大概是怎样的
- 从源码看,Activity 的启动过程(
startActivity()
)相当复杂,涉及 Instrumentation、ActivityThread 和 ActivityManagerService(AMS) 等 - 简单理解,启动 Activity 的请求会由 Instrumentation 来处理,然后它通过 Binder 向 AMS 发请求
- AMS 内部维护者一个 ActivityStack 并负责栈内的 Activity 的状态同步,AMS 通过 ActivityThread 去同步 Activity 的状态从而完成生命周期方法的调用
6. 几个常见场景下 Activity 生命周期方法调用顺序
Activity 正常启动与结束
onCreate()
—>onStart()
—>onResume()
—>onPause()
—>onStop()
—>onDestroy()
Activity 之间的跳转:从 Activity A 跳转到 Activity B
A: onPause()
—>
B: onCreate()
—>
B: onStart()
—>
B: onResume()
A: onStop()
Activity 之间的跳转:从 Activity B 再返回到 Activity A
B: onPause()
—>
A: onRestart()
—>
A: onStart()
—>
A: onResume()
B: onStop()
B: onDestroy()
- IdleHandler 在 Activity 生命周期方法
onStop()/onDestroy()
中的一个应用:Activity 生命周期方法 10s 回调的兜底机制
- IdleHandler 在 Activity 生命周期方法
屏幕旋转(开启了允许旋转的 flag)
onPause()
—>onStop()
—>onSaveInstanceState()
—>onDestroy()
onCreate()
—>onStart()
—>onRestoreInstanceState()
—>onResume()
后台 Activity 被系统杀死并重建
Activity 切到后台不可见时: onSaveInstanceState()
—>onDestroy()
onCreate()
—>onStart()
—>onRestoreInstanceState()
—>onResume()
具有返回值的 Activity 的启动与返回
A: startActivityForResult()
—>
B: onPause()
———>
A: onActivityResult()
———>
A: onRestart()
———>
A: onStart()
———>
A: onResume()
—>
B: onStop()
—>
B: onDestroy()
onNewIntent() 方法的调用时机(涉及到启动模式)
7. 异常情况 Activity 生命周期包括哪些异常情况
- 资源相关的系统配置发生改变导致 Activity 被杀死并被重新创建
- 系统资源内存不足导致低优先级的 Activity 被杀死
8. 系统配置发生改变的异常情况下 Activity 的重建过程
系统配置发生改变后,Activity 会被销毁,其
onPause()
、onStop()
、onDestroy()
方法均会被调用由于 Activity 是在异常情况下终止的,系统会调用
onSaveInstanceState()
方法来保存当前 Activity 的状态从源码方法的注释可知,在 API 28 (Android 9 Pie) 及更高版本
onSaveInstanceState()
方法的调用时机是在onStop()
之后;在 API 28 (Android 9 Pie) 之前的版本调用时机是在onStop()
之前,同时它和onPause()
没有既定的时序关系,既可能在onPause()
之前调用,也可能在onPause()
之后调用看源码的时候看到下面这段感觉挺奇怪,查了下才知道是电影《星际迷航》里的台词,Google 的开发者也是任性:
- 国内的各种规范估计是学不来的,有被约茶的风险
- 特意查了下,Google 源码仓库 master 分支里没有这一段
当 Activity 被重新创建后,系统会调用
onRestoreInstanceState()
方法,并且把 Activity 销毁时onSaveInstanceState()
方法所保存的 Bundle 对象作为参数同时传递给onRestoreInstanceState()
方法和onCreate()
方法- 可以通过
onRestoreInstanceState()
和onCreate()
方法来判断 Activity 是否被重建了,如果被重建了,那么就可以取出之前保存的数据并恢复 - 从时序上来说,
onRestoreIntanceState()
的调用时机在onStart()
之后
- 可以通过
在
onSaveInstanceState()
和onRestoreInstanceState()
方法中,系统自动做了一定程度的恢复工作- 当 Activity 在异常情况下需要重新创建时,系统会默认为我们保存当前 Activity 的视图结构,并且在 Activity 重启后为我们恢复这些数据。比如文本框中用户输入的数据、ListView 滚动的位置等,这些 View 相关的状态系统都能够默认为我们恢复
- 具体针对某一个特定的 View 系统能为我们恢复哪些数据,我们可以查看 View 的源码。和 Activity 一样,每个 View 都有
onSaveInstanceState()
方法和onRestoreInstanceState()
方法,看一下它们的具体实现,就能知道系统能自动为每个 View 恢复哪些数据
系统在 Activity 异常终止的时候会调用
onSaveInstanceState()
和onRestoreInstanceState()
来存储和恢复数据。按 Home 键或启动新 Activity 时会单独触发onSaveInstanceState()
方法的调用
9. 关于保存和恢复 View 层次结构,系统的工作流程
- 首先 Activity 被意外终止时,Activity 会调用
onSaveInstanceState()
方法去保存数据,然后 Activity 会委托 Window 去保存数据,接着 Window 再委托它上面的顶级容器去保存数据 - 顶层容器是一个 ViewGroup,一般来说它很可能是 DecorView,最后顶层容器再去一一通知它的子元素来保存数据,这样整个数据保存过程就完成了
- 这是一种典型的委托思想,上层委托底层、父容器委托子元素去处理一件事情。这种思想在 Android 中有很多应用,比如 View 的绘制过程、事件分发等都是采用类似的思想
- 数据恢复过程也是采用的类似的委托思想
10. 异常情况下 Activity 被销毁并重新创建,获取保存的数据时,接受的位置可以选择 onRestoreInstanceState() 或者 onCreate(),二者的区别
onRestoreInstanceState()
一旦被调用,其参数Bundle savedInstanceState
一定是有值的,不用额外判空- 而
onCreate()
如果是正常启动的话,其参数Bundle savedInstanceState
为null
,所以必须要进行判空操作 - 这两个方法我们可以选择任意一个进行数据恢复,但是官方文档建议采用
onRestoreInstanceState()
方法去恢复数据
11. 系统资源内存不足导致低优先级的 Activity 被杀死,此异常情况下的数据存储和恢复过程
- 这种情况不太好模拟,但是其数据存储和恢复过程和系统配置改变导致 Activity 被销毁并重新创建的异常情况下的数据存储和恢复过程完全一致
- 当系统内存不足时,系统就会按照优先级去杀死目标 Activity 所在的进程,并在后续通过
onSaveInstanceState()
和onRestoreInstanceState()
来存储和恢复数据- 如果一个进程中没有四大组件在执行,那么这个进程将很快被系统杀死。因此,一些后台工作不适合脱离四大组件而独自运行在后台中,这样进程很容易被杀死
- 比较好的方法是将后台工作放入 Service 中从而保证进程有一定的优先级,这样就不会轻易地被系统杀死
12. Activity 按照优先级从高到低分类
- 前台 Activity: 正在和用户交互的 Activity,优先级最高
- 可见但非前台 Activity: 比如 Activity 中弹出了一个对话框,导致 Activity 可见但是位于后台无法和用户直接交互
- 后台 Activity: 已经被暂停的 Activity,比如执行了 onStop(),优先级最低
13. 默认情况下当系统配置发生了改变,Activity 就会被销毁并重新创建,怎样能不重新创建
- 可以在 AndroidMenifest.xml 清单文件中给 Activity 的 configChanges 属性指定属性值为 orientation
- 编译时项目中指定的 minSdkVersion 和 targetSdkVersion 如果有一个大于 13,此时为了防止旋转屏幕时 Activity 重启,除了 orientation,还要加上 screenSize 属性值。即:android:configChanges=”orientation|screenSize”
- 指定好属性值后,Activity 的确没有重新创建,也没有调用 onSaveInstanceState() 和 onRestoreInstanceState() 来存储和恢复数据。而是系统调用了 Activity 的
onConfigurationChanged()
方法,我们可以在这个方法里加入自己的逻辑
14. configChanges 的属性值和对应的含义
属性值 | 含义 |
---|---|
mcc | SIM 卡唯一标识 IMSI(国际移动用户识别码)中的国家代码,由三位数字组成,中国为 460。此项标识 mcc 代码发生了改变 |
mnc | SIM 卡唯一标识(国际移动用户识别码)中的运营商代码,由两位数字组成,中国移动 TD 系统为 00,中国联通为 01,中国电信为 03。此项标识 mnc 发生了改变 |
locale | 设备的本地位置发生了改变,一般指切换了系统语言 |
touchscreen | 触摸屏发生了改变,这个很费解,正常情况下无法发生,可以忽略 |
keyboard | 键盘类型发生了改变,比如用户使用了外插键盘 |
keyboardHidden | 键盘的可访问性发生了改变,比如用户调出了键盘 |
navigation | 键盘导航方式发生了改变,比如采用了轨迹球导航,这个有点费解,很难发生,可以忽略 |
screenLayout | 屏幕布局发生了改变,很可能是用户激活了另外一个显示设备 |
fontScale | 系统字体缩放比例发生了改变,比如用户选择了一个新字号 |
uiMode | 用户界面模式发生了改变,比如是否开启了夜间模式(API 8 新加) |
orientation | 屏幕方向发生了改变,这个是最常用的,比如旋转了手机屏幕 |
screenSize | 当屏幕的尺寸信息发生了改变,当旋转设备屏幕时,屏幕尺寸会发生变化。这个选项比较特殊,它和编译选项有关,当编译选项中的 minSdkVersion 和 targetSdkVersion 均低于 13 时,此选项不会导致 Activity 重启,否则会导致 Activity 重启(API 13 新加) |
smallestScreenSize | 设备的物理屏幕尺寸发生改变,这个属性和屏幕的方向没关系,仅仅表示在实际的物理屏幕的尺寸改变的时候发生,比如用户切换到了外部的显示设备。这个属性和 screenSize 一样,当编译选项中的 minSdkVersion 和 targetSdkVersion 均低于 13 时,此选项不会导致 Activity 重启,否则会导致 Activity 重启(API 13 新加) |
layoutDirection | 当布局方向发生变化,这个属性用的比较少,正常情况下无须修改布局的 layoutDirection 属性(API 17 新加) |