0%

Activity 的生命周期和启动模式(一):Activity 的生命周期全面分析

1. Activity 生命周期的种类

  • 典型情况下的生命周期:在有用户参与的情况下,Activity 所经历的生命周期过程
  • 异常情况下的生命周期:由于内存资源不足 Activity 被系统回收 或者 由于当前设备的 Configuration 发生改变从而导致 Activity 被销毁重建

2. 典型情况下 Activity 生命周期各个方法含义

  1. onCreate():

    • 表示 Activity 正在被创建,这是生命周期的第一个方法
    • 在这个方法中可以做一些初始化工作,比如调用 setContentView() 去加载界面布局资源、初始化 Activity 所需数据等
  2. onRestart():

    • 表示 Activity 正在被重新启动
    • 一般情况下,当当前 Activity 从不可见新变为可见状态时,onRestart() 方法就会被调用
    • 这种情形一般是用户行为导致的,比如用户按 Home 键切换到桌面或者用户打开了一个新的 Activity,这时当前的 Activity 就会暂停,也就是 onPause()onStop() 方法被执行了,接着用户又回到了这个 Activity,此时 onRestart() 方法就会被回调
  3. onStart():

    • 表示 Activity 正在被启动,即将开始
    • 这时 Activity 已经可见了,但是还没有出现在前台,还无法和用户交互。这个时候其实可以理解为 Activity 已经显示出来了,但用户还看不到
  4. onResume():

    • 表示 Activity 已经可见了,并且出现在前台并开始活动
    • 要注意和 onStart() 方法的对比,onStart()onResume() 都表示 Activity 已经可见,但是 onStart() 的时候 Activity 还在后台,onResume() 的时候 Activity 才显示到前台
  5. onPause():

    • 表示 Activity 正在停止,正常情况下,紧接着 onStop() 就会被调用
    • 在特殊情况下,如果这个时候再回到当前 Activity,那么 onResume() 会被调用
    • 从方法源码注释可知,此时可以做一些存储数据停止动画等工作,但是注意不能太耗时,因为这会影响到 Activity 的显示,onPause() 必须先执行完,新 Activity 的 onResume() 才会执行
  6. onStop():

    • 表示 Activity 即将停止
    • 可以做一些稍微重量级的回收工作,同样不能太耗时
  7. 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())相当复杂,涉及 InstrumentationActivityThreadActivityManagerService(AMS)
  • 简单理解,启动 Activity 的请求会由 Instrumentation 来处理,然后它通过 BinderAMS 发请求
  • AMS 内部维护者一个 ActivityStack 并负责栈内的 Activity 的状态同步,AMS 通过 ActivityThread 去同步 Activity 的状态从而完成生命周期方法的调用

6. 几个常见场景下 Activity 生命周期方法调用顺序

  1. Activity 正常启动与结束

    onCreate() —> onStart() —> onResume() —> onPause() —> onStop() —> onDestroy()

  2. Activity 之间的跳转:从 Activity A 跳转到 Activity B

    A: onPause()

    —> B: onCreate()

    —> B: onStart()

    —> B: onResume()

    A: onStop()

  3. Activity 之间的跳转:从 Activity B 再返回到 Activity A

    B: onPause()

    —> A: onRestart()

    —> A: onStart()

    —> A: onResume()

    B: onStop()

    B: onDestroy()

  4. 屏幕旋转(开启了允许旋转的 flag)

    onPause() —> onStop() —> onSaveInstanceState() —> onDestroy()

    onCreate() —> onStart() —> onRestoreInstanceState() —> onResume()

  5. 后台 Activity 被系统杀死并重建

    Activity 切到后台不可见时: onSaveInstanceState() —> onDestroy()

    onCreate() —> onStart() —> onRestoreInstanceState() —> onResume()

  6. 具有返回值的 Activity 的启动与返回

    A: startActivityForResult()

    —> B: onPause()

    ———> A: onActivityResult()

    ———> A: onRestart()

    ———> A: onStart()

    ———> A: onResume()

    —> B: onStop()

    —> B: onDestroy()

  7. 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 的开发者也是任性:

      《星际迷航》

  • 当 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 savedInstanceStatenull,所以必须要进行判空操作
  • 这两个方法我们可以选择任意一个进行数据恢复,但是官方文档建议采用 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 新加)
-------------------- 本文结束感谢您的阅读 --------------------