没错,我们可以选择躺在地上赖着不动:反正我的起点低,反正我家庭不如别人家,有无数个“反正”等着你,但是没有人会看你一眼,每个人都在前行;当别人在飞翔,你依然躺在地上,恐怕你一生的时间都只能用来数伤口了.
本节将Activity的生命周期分为两部分内容,一部分是正常情况下的生命周期,另一部分是异常情况下的生命周期。所谓正常情况下的生命周期,是指在有用户参与的情况下,Activity所经过的生命周期的改变;而异常情况下的生命周期是指Activity被系统回收或者由于当前设备的Configuration发生改变从而导致Activity被销毁重建,异常情况下的生命周期的关注点和正常情况下略有不同。
1. 正常情况下的生命周期分析
在正常情况下,Activity会经历如下生命周期。
(1)onCreate:表示Activity正在被创建,这是生命周期的第一个方法。在这个方法中,可以做一些初始化操作,比如调用setContentView方法去加载界面布局资源、初始化Activity所需数据等。(只可能调用一次)
(2)onRestart:表示Activity正在重新启动。一般情况下,当当前Activity从不可见重新变为可见状态时,onRestart方法就会被调用。这种情形一般是用户行为所导致的,比如用户按Home键切换到桌面或者用户打开了一个新的Activity,这时当前的Activity就会暂停,也就是onPause和onStop方法被执行了,接着用户又回到了这个Activity,就会出现这种情况。(该方法可调用一次以上)
(3)onStart:表示Activity正在被启动,即将开始,这时Activity已经可见(显示)了,但是没有出现在前台,还无法和用户交互。这个时候其实可以理解为Activity已经显示出来了,但是我们还看不到。(该方法可调用一次以上)
(4)onResume:表示Activity已经可见了,并且出现在前台并开始活动。要注意onResume和onStart的对比,两者都表示Activity已经可见了,但是onStart的时候Activity还在后台,onResume的时候Activity才显示到前台。(该方法可调用一次以上)
(5)onPause:表示Activity正在停止,正常情况下,紧接着onStop方法就会被调用。在特殊情况下,如果这个时候快速地再回到当前Activity,那么onResume方法会被调用。笔者的理解是,这种情况下属于极端情况,用户操作很难重现这一场景。此时可以做一些存储数据、停止动画等工作,但是注意不能在onPause方法中做太耗时的操作,因为这会影响到新Activity的显示,onPause方法必须先执行完,新Activity中的onResume方法才会执行。(该方法可调用一次以上)
(6)onStop:表示Activity即将停止,可以做一些稍微重量级的回收工作,同样不能太耗时。(该方法可调用一次以上)
(7)onDestory:表示Activity即将被销毁,这是Activity生命周期中的最后一个回调,在这里,我们可以做一些回收工作和最终的资源释放。(只可能调用一次)
正常情况下,Activity的常用生命周期就只有上面7个,图1-1更详细地描述了Activity各种生命周期的切换过程。
针对图1-1,分如下几种情况:
(1)针对一个特定的Activity,第一次启动,回调如下:onCreate –> onStart –> onResume。
(2)当用户打开新的Activity或者切换到桌面的时候,回调如下:onPause –> onStop。这里有一种特殊情况,如果新Activity采用了透明主题,那么当前Activity不会回调onStop。
(3)当用户再次回到原Activity时,回调如下:onRestart –> onStart –> onResume。
(4)当用户按back键回退时,回调如下:onPause –> onStop –> onDestroy。
(5)当Activity被系统回收后再次打开,生命周期方法回调过程和(1)一样,注意只是生命周期方法一样,不代表所有过程都一样。
这个问题下节会详细说明。
思考:
问题一:onStart和onResume、onPause和onStop从描述上来看差不多,对我们来说有什么实质的不同呢?
从生命周期上来分析,这两个配对的回调分别表示不同的意义,onStart和onStop是从Activity是否可见这个角度来回调的,而onResume和onPause是从Activity是否位于前台这个角度来回调的,除了这种区别,在实际使用中没有其他明显区别。
问题二:假设当前Activity为A,如果这时用户打开一个新的ActivityB,那么B的onResume和A的onPause哪个现执行呢?
这个问题可以从源码里得到解释,从Activity的启动过程来看,涉及Instrumentation、ActivityThread和ActivityManagerService。简单理解,启动Activity的请求会由Instrumentation来处理,然后它通过Binder向ActivityManagerService发送请求,ActivityManagerService内部维护着一个ActivityStack并负责栈内的Activity的状态同步,ActivityManagerService通过ActivityThread去同步Activity的状态从而完成生命周期方法的调用。在ActivityStack中的resumeTopActivityInnerLocked方法中,有这么一段代码:
从上述代码中可以看出,在新Activity启动之前,栈顶的Activity需要先onPause后,新Activity才能启动。最终,在ActivityStackSupervisor中的realStartActivityLocked方法会调用如下代码:
从上述代码中可以看出,app.thread的类型是IApplicationThread,而IApplicationThread的具体实现是ActivityThread中的ApplicationThread。所以,这段代码实际上调到了ActivityThread中的内部类ApplicationThread中的scheduleLaunchActivity方法,而scheduleLaunchActivity方法最终会完成新Activity的onCreate、onStart、onResume方法的调用过程。
因此,得出结论:旧Activity先调用onPause,然后新Activity再启动。
回答了第二个问题之后,大伙可能又要问了:ApplicationThread的scheduleLaunchActivity方法为什么会完成新Activity的onCreate、onStart、onResume的调用过程呢?
因为在ApplicationThread的scheduleLaunchActivity方法中最终会调用如下方法,而如下方法的确会完成onCreate、onStart、onResume的调用过程。
到底是不是我所说的:“旧Activity先调用onPause,然后新Activity再启动”。我们通过例子验证一下!
MainActivity.java
|
|
SecondActivity.java
|
|
大伙来看一下log,是不是和上面分析的一样一样的,如图1-2:
扩展一下,那如果在MainActivity类中加入了一行调用finish()方法的代码呢?来看log:
一切如此这般!
Android官方文档对onPause方法的解释有这么一句话:不能在onPause方法中做重要级的操作,因为必须在onPause方法执行完成以后新Activity才能Resume,从这一点也能间接证明我们的结论。通过分析这个问题,我们知道onPause方法和onStop方法都不能执行耗时的操作,尤其是onPause方法,这也意味着,我们应当尽量在onStop方法中做操作,从而使得新Activity尽快显示出来并切换到前台。
2. 异常情况下的生命周期分析
我们知道,Activity除了受用户操作所导致的正常的生命周期方法调度以外,还有一些异常情况,比如当资源相关的系统配置发生改变以及系统内存不足时,Activity就可能被kill。下面我们具体分析这两种情况。
2.1 情况1:资源相关的系统配置发生改变导致Activity被kill并重新创建
那么这种情况比如说:当前Activity处于竖屏状态,如果突然旋转屏幕,由于系统配置发生了改变,在默认情况下,Activity就会被销毁并且重新创建,当然我们也可以阻止系统重新创建我们的Activity。
在默认情况下,如果我们的Activity不做特殊处理,那么当系统配置发生改变后,Activity就会被销毁并重新创建,其生命周期如图1-3所示:
当系统配置发送改变后,Activity会被销毁,其onPause、onStop、onDestory均会被调用,同时由于Activity是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态。这个方法的调用时机是在onStop之前,它和onPause没有既定的时序关系,它既可能在onPause之前调用,也可能在onPause之后调用。需要强调的一点是,这个方法只有出现在Activity被异常终止的情况下,正常情况下系统不会回调这个方法。当Activity被重新创建后,系统会调用onRestoreInstanceState,并且把Activity销毁时onSaveInstanceState方法所保存的Bundle对象作为参数同时传递给onRestoreInstanceState合onCreate方法。因此,我们通过这两个方法来判断Activity是否被重建了(判断Bundle是否为空,只需要在onCreate方法中进行判断即可,因为一旦onRestoreInstanceState被调用其Bundle值一定不为Null,这也是onRestoreInstanceState方法和onCreate方法的区别!但官方文档建议是采用onRestoreInstanceState去恢复数据),如果被重建了,那么我们就可以取出之前保存的数据并恢复,从时序上来说,onRestoreInstanceState的调用时机在onStart之后。
关于保存和恢复View层次结构(数据),系统的工作流程是这样的:首先Activity被意外终止,Activity会调用onSaveInstanceState去保存数据,然后Activity会委托Window去保存数据,接着Window再委托它上面的顶级容器去保存数据。顶级容器是一个ViewGroup,一般来说它可能是DecorView。最后顶层容器再去一一通知它的子元素来保存各自数据,这样整个数据保存过程就完成了。可以发现,这是一种典型的委托思想,上层委托下层、父容器委托子元素去处理一件事情,这种思想在Android中有很多应用,比如View的绘制过程、事件分发等都是采用类似的思想。那么关于数据恢复过程也是类似的,这就不多说了。
我们知道,当系统配置发送改变之后,Activity会被重新创建,那么有没有什么办法不重新创建呢?
答案是:
我们只需要在清单文件中,在对应的activity配置属性里面添加:
|
|
这样的话,Activity不会重新创建,并且也不会调用onSaveInstanceState和onRestoreInstanceState方法来存储和恢复数据,取而代之的是系统调用了Activity的onConfigurationChanged方法,这个时候我们就可以做一些自己的特殊处理了。
2.2 情况2:资源内存不足导致低优先级的Activity被kill
Activity按照优先级从高到底,可以分为如下三种情况:
(1)前台Activity —— 正在和用户交互的Activity,优先级最高。
(2)可见但非前台Activity —— 比如Activity中弹出了一个对话框,导致Activity可见但是位于后台无法和用户直接交互。
(3)后台Activity —— 已经被暂停的Activity,比如执行了onStop,优先级最低。
Android进程优先级顺序(由低到高):
(1)空进程 —— 这是Android系统优先杀死的,因为此时该进程已经没有任何用途。
(2)后台进程 —— 包含不可见的Activity,即跳转到其他activity后,由于资源不足,系统会将原来的activity杀死(即跳转的来源)
(3)服务进程 —— 即Service,当系统资源不足时,系统可能会杀掉正在执行任务的Service。因此在Service执行比较耗时的操作,并不能保证一定能执行完毕。
(4)可见进程 —— 当前屏幕上可以看到的activity,例如显示一个对话框的activity,那么对话框变成了前台进程,而调用他的activity是可见进程,但并不是前台的。
(5)前台进程 —— 当前处于最前端的activity,也就是Android最后考虑杀死的对象。一般来说,前台进程Android系统是不会杀死的,只有当前4个都杀掉资源依旧不够才可能会发生。
当系统内存不足时,系统就会按照上述优先级去杀死目标Activity所在的进程。