用「机器人模型理论」解释Activity的生命周期

前记

Activity 是四大组件之一,又是开发者遇到最多的组件,重要性不言而喻。大多数博客或者书籍中关于其生命周期的讲解也都大同小异,大多基于Android 官方文档 ,而后者无疑是最权威的。不过这里我还是想推荐一篇优秀的博文,魏祝林《两分钟彻底让你明白Android Activity生命周期(图文)!》,图文并茂,讲解细致,值得一读。

不过,我是从另外一个角度出发思考这个问题的。Android 生命周期中的七个方法个数是确定的,同时执行顺序也是固定的(绝大多数情况)。为什么方法个数不能增减?为什么执行顺序不可以改变?我想,这些问题也许我们回答不了,但是编写Android 底层代码的大牛程序员们肯定可以解释,他们也是人,只不过很牛而已。他们的依据我想也是来源于生活,对现实世界的规律进行抽像、总结、简化,因为思想是通用的。同时,我发现几乎所有的参考资料都是对理论的一通分析解读。于是,我想我是不是可以用生活中的例子来解释这一基础原理。终于,在憋出内伤之前,憋出了一个自创的「机器人模型理论」,Android 的本意不就是机器人么。不足之处,还请批评指正~


官方生命周期图

经典生命周期图

Activity 生命周期图

由上到下,完整全面,尤其包括了App process killed的特殊情况。

金字塔型周期图

Activity 生命周期图

7种状态、9个方法。从左到右,更加形象,易于理解。下面的分析以此图为例。


机器人模型理论

啰啰嗦嗦这么多,现在开始进入正题,我以上图中金字塔模型 Activity 的生命周期图为例:把一个完整的 Activity 生命周期的过程想象成一个机器人A从零件组装到整个拆解的一个过程,这个机器人被组件的目的是为了帮助主人进行一些劳动,当完成某项具体工作我们则把它拆解继续组装下一个有其它特定功能的机器人B,以此类推。(下文中的–>代替「对应」二字,视觉体验更好)

  1. 最左边的机器人图标 –> 一堆崭新的A机器人零件,这些基础零件代表组成一个 Activity 所必须的各种资源,比如布局文件、图片资源等。
  2. onCreate()方法 –> A机器人的零件被组装的过程,就像工厂中高效的流水线加工方式一样,整个过程是动态的,速度较快。
  3. Created –> A机器人被整体组装完成,注意单词「Created」在英文语法中是过去时,或者理解成分词,表示一种相对稳定的状态(这里为什么不用其它的分词形式肯定也是有考究的)。说明整个机器人已经处于就绪状态,只要主人一声令下(这个命令可能是按下电源开关、按下开始按钮)就会马上投入工作。
  4. onStart()方法 –> 主人按下A机器人电源开关,相当于按下机器人身上自带的电池开关按钮。
  5. Started(visible) –> 按下电源开关后,A机器人身上的电源指示灯亮起,电源指示灯亮起就好像整个机器人可见了一样,通电之前整个机器人是暗淡无光的,好像不能被真正看到。
  6. onResume()方法 –> 按下「开始/暂停」按钮,A机器人身上的开始指示灯亮起,为了避免误触发,在通电之后还应该有一个「开始/暂停」按钮,通电之后再按下这个按钮机器人才能真正开始工作。
  7. Resumed(visible) –> 电源指示灯和开始指示灯同时亮起,A机器人才开始运转工作,此时机器人处于 running 状态,开始扫地。
  8. onPause()方法 –> 按下「开始/暂停」按钮,暂停A机器人的工作,并不是所有事情都会向预想的方向发展,总会有意外情况。比方说在这里主人突然想让另外一个机器人B(已经经历了前面的几个步骤)拖地,这个时候A机器人就必须要停下来待命同时要保留之前工作的成果。
  9. Paused(partially visible) –> 开始指示灯灭,但电源指示灯依然亮起,此时A机器人处于待命状态,相当于部分可见。
    • onResume()方法 –> 情况1:按下A机器人「开始/暂停」按钮,两个指示灯又同时亮起,让A机器人继续工作,这个时候就不让B机器人工作了。多个机器人同时存在时,只能有一个机器人处于工作状态,其他机器人要么处于暂停待命状态,要么处于停止状态。
    • onStop()方法+Stopped(hidden) –> 情况2:按下A机器人电源按钮,此时A机器人两个灯都关闭,A机器人由待命状态变成停止状态相当于完全不可见,但仍然存在,并没有被拆解销毁。
      • onDestroy()方法+Destroyed –> 情况2.1:拆解A机器人,零件散落一地,A机器人完成工作,则把它拆解掉用于组装成具有其它功能的机器人,提高机器人零件的复用率,此时A机器人终于走到了生命的尽头。
      • onRestart()方法+onStart()方法 –> 情况2.2:再次按下A机器人电源按钮,使A机器人由停止状态变成待命状态,然后执行 onResume()方法,两个指示灯同时亮起,达到 Resume 状态,即工作状态。

注意:上述情况1和情况2是并列关系,不是先后顺序的关系,更不是同时发生的顺序。情况2.1和情况2.2同理,即某一条件下,只能有唯一一种情况发生。

需要注意的地方

  • Android 是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合,这个栈也被称作返回栈(Back Stack),系统总是会显示处于栈顶的活动给用户。另外,一个 Task 中的 Activity 可以来自不同的 App,同一个 App 的Activity 也可能不在一个 Task 中。
  • Activity 的构成并不是一个 Activity 对象再加上一个布局文件那么简单,在 Activity 和开发人员设置的视图之间还隔着两层。实际上视图会被设置给一个 Window 类,这个 Window 中含有一个 DecorView,这个 DecorView 才是整个窗口的顶级视图
  • onStart()方法和 onStop()方法是从 Activity 是否可见这个角度来回掉的,onResume()方法和 onPause()方法是从 Activity 是否位于前台这个角度来回调的。
  • 在一个 aty 中启动另一个 aty 的生命周期方法执行顺序,有两种情况:
    1. 当 B aty 不透明完全盖住 A aty ,A 不能显示时:
      A onPause-->B onCreate-->B onStart-->B onResume …… A onStop
      即,在 B 呈现出来之前 A 还是可以呈现的,这也与常识相符。只有在 B 完全呈现出来之后 A 才会执行 onStop 方法。
    2. 当 B aty 部分透明,比如对话框,此时 A 仍可以显示时:
      B onPause-->A onResume-->B onStop-->B onDestroy
      即,A 不会执行 onStop 方法,还是处于运行状态。点击空白处关闭对话框时,B 暂停,A 立即恢复 resume。
  • 为保证用户体验,两个 Activity 之间切换时间不会太久,我们最好不要在 onPause()中做重量级的操作,因为如上条所说必须 onPause()执行完成以后新 Activity 才能 Resume。
  • 特殊情况①,当资源相关的系统配置发送改变,比如突然旋转屏幕,Activity 会被销毁(其 onPause、onStop、onDestroy方法均会被调用)并重建,同时为保证用户体验系统会调用 onSaveInstanceState方法来保存当前 Activity 的状态以方便后续被重建。这个方法调用时机在 onStop 之前但和 onPause 没有既定的时序关系。若被重建,此方法保存的 Bundle 对象状态信息会作为参数同时传递给 onRestorInstanceState 和 onCreate方法,这也是 onCreate方法中参数Bundle savedInstanceState的来源。
  • 特殊情况②,后台运行太多程序导致系统内存资源不足时会导致优先级低的 Activity 被杀死(这里杀死、销毁、回收是一个概念),此时数据的存储和恢复过程和上一种特殊情况完全一致。Activity 按照优先级从高到低排序依次为:前台 Activity > 可见非前台 Activity(对话框形式)> 不可见后台 Activity,这一点很容易理解。系统只在 Activity 异常终止的时候才会调用 onSaveInstanceStateonRestoreInstanceState来存储和恢复数据,其他情况不会触发这个过程但是按Home键或者启动新Activity仍然会单独触发onSaveInstanceState的调用

  • 如何实现一个符合用户期待的app,我们需要注意下面几点:使用app的时候,不会因为有来电通话或者切换到其他app而导致程序crash;用户没有激活某个组件时不会消耗宝贵的系统资源;离开app并且一段时间后返回,不会丢失用户的使用进度;设备发生屏幕旋转时不会crash或者丢失用户的使用进度。
  • 7中状态中,不考虑生和死的状态,就其它5中状态而言,Created与Started 是短暂的,即在系统调用onCreate(), 之后会迅速调用onStart(), 之后再迅速执行onResume()。而状态 Resumed、Paused 和 Stopped 是可以存在一段比较长的时间的
  • 出于用户体验考虑,onCreate里面尽量少做事情,避免程序启动太久都看不到界面。同时,如果activity含有在onCreate调用时创建的后台线程,或者是其他有可能导致内存泄漏的资源,则应该在OnDestroy()时进行资源清理,杀死后台线程。
  • 并不是生命周期图中所有方法都会执行,除非程序在onCreate()方法里面就调用了finish()方法,系统通常是在执行了onPause()与onStop() 之后再调用onDestroy() 。在某些情况下,例如我们的activity只是做了一个临时的逻辑跳转的功能,它只是用来决定跳转到哪一个activity,这样的话,需要在onCreate()里面调用finish()方法,这样系统会直接调用onDestory(),跳过生命周期中的其他方法。
  • 当系统调用activity中的onPause(),通常应该在onPause()回调方法里面做以下事情:停止动画或者是其他正在运行的操作,那些都会导致CPU的浪费;提交在用户离开时期待保存的内容(例如邮件草稿);释放系统资源,例如 Camera、broadcast receivers, sensors (比如GPS), 或者是其他任何会影响到电量的资源。总之,onPause()会是一个比较好的地方去做那些释放资源的操作。但是,我们应该避免在onPause()时执行CPU-intensive 的工作,例如写数据到DB,因为它会导致切换到下一个activity变得缓慢(应该把那些heavy-load的工作放到onStop去做)。
  • 恰当的停止与重启我们的activity是很重要的,在activity生命周期中,他们能确保用户感知到程序的存在并不会丢失他们的进度。在下面一些关键的场景中会涉及到停止与重启:用户打开最近使用app的菜单并从我们的app切换到另外一个app,这个时候我们的app是被停止的。如果用户通过手机主界面的启动程序图标或者最近使用程序的窗口回到我们的app,那么我们的activity会重启;用户在我们的app里面执行启动一个新activity的操作,当前activity会在第二个activity被创建后stop。如果用户点击back按钮,第一个activtiy会被重启;用户在使用我们的app时接收到一个来电通话.
  • 系统在activity停止时会在内存中保存Activity的实例,所以有时不需要实现onStop(),onRestart()甚至是onStart()方法. 因为大多数的activity相对比较简单,activity会自己停止与重启,我们只需要使用onPause()来停止正在运行的动作并断开系统资源链接。需要注意的是,无论什么原因导致activity停止,系统总是会在onStop()之前调用onPause()方法
  • 当activity调用onStop()方法, activity不再可见,并且应该释放那些不再需要的所有资源。一旦activity停止了,系统会在需要内存空间时摧毁它的实例(和栈结构有关,通常back操作会导致前一个activity被销毁)。极端情况下,系统会直接杀死我们的app进程(拆东墙补西墙的做法),并不执行activity的onDestroy()回调方法, 因此我们需要使用onStop()来释放资源,从而避免内存泄漏保证良好的用户体验。
  • onRestart()方法是只在activity从stopped状态恢复时才会被调用,因此我们可以使用它来执行一些特殊的恢复(restoration)工作,请注意之前是被stopped而不是destrory。同时,用户很可能在回到这个activity之前已经过了很长一段时间,所以onStart()方法是一个比较好的地方来验证某些必须的系统特性是否可用。即:onStop()方法应该做清除所有activity资源的操作。onDestory方法是我们最后去清除那些可能导致内存泄漏的操作
  • 我们也可以选择实现 onRestoreInstanceState() ,而不是在onCreate方法里面恢复数据。 onRestoreInstanceState()方法会在 onStart() 方法之后执行。系统仅仅会在存在需要恢复的状态信息时才会调用 onRestoreInstanceState() ,因此不需要检查 Bundle 是否为null。

上述一些知识点可能并不适合入门,可以在进阶阶段参考复习。同时,由于时间关系就不贴代码了,后续尽量图文并行或者Gif动图等

后记

自己在头脑中想的话还是挺简单的,无非就是一个机器人从组装到拆解的过程,中间可能被打断进而被提前拆解也有可能一直正常工作下去直到工作完成并正常被拆解,没想到写成文字出来字数还不少。总之,目的就一个:既然 Activity 这么重要,那就无论何时何地都要在充分理解的基础上完整准确地想出、画出、说出它的整个生命周期图–7种状态、9个方法,做到百分百掌握。现在看来,也没有什么难的,不知道各位是否认同我上面的「机器人模型理论」。就像看一本理论书籍,把书读厚再把书读薄,基本上就问题不大了。

「凡事经过深入思考都会变得相对简单」,共勉!