Android的生命周期
典型情况下的生命周期
如上图所示,正常情况下,Activity会经历以下的生命周期
1. onCreate : 与onDestroy配对,表示Activity正在被创建,这是生命周期的第一个方法。在这个方法中可以做一些初始化的工作(加载布局资源、初始化Activity所需要的数据等),耗时的工作在异步线程上完成。
2. onRestart : 表示Activity正在重新启动。一般情况下,在当前Activity从不可见重新变为可见的状态时onRestart就会被调用。这种情形一般是由于用户的行为所导致的,比如用户按下Home键切换到桌面或者打开了一个新的Activity(这时当前Activity会暂停,也就是onPause和onStop被执行),接着用户有回到了这个Activity,就会出现这种情况。
3. onStart : 与onStop配对,表示Activity正在被启动,并且即将开始。但是这个时候要注意它与onResume的区别。两者都表示Activity可见,但是onStart时Activity还正在加载其他内容,正在向我们展示,用户还无法看到,即无法交互。
4. onResume : 与onPause配对,表示Activity已经创建完成,并且可以开始活动了,这个时候用户已经可以看到界面了,并且即将与用户交互(完成该周期之后便可以响应用户的交互事件了)。
5. onPause : 与onResume配对,表示Activity正在暂停,正常情况下,onStop接着就会被调用。在特殊情况下,如果这个时候用户快速地再回到当前的Activity,那么onResume会被调用(极端情况)。一般来说,在这个生命周期状态下,可以做一些存储数据、停止动画的工作,但是不能太耗时,如果是由于启动新的Activity而唤醒的该状态,那会影响到新Activity的显示,原因是onPause必须执行完,新的Activity的onResume才会执行。
6. onStop : 与onStart配对,表示Activity即将停止,可以做一些稍微重量级的回收工作,同样也不能太耗时(可以比onPause稍微好一点)。
7. onDestory : 与onCreate配对,表示Activity即将被销毁,这是Activity生命周期的最后一个回调,我们可以做一些回收工作和最终的资源释放。
这里提出两个问题:
1.onStart和onResume、onPause和onStop从描述上来看差不多,对我们来说有什么实质上的不同呢?
2.假设当前Activity为A,如果这时用户打开了一个新的活动B,那么B的onResume和A的onPause谁先执行呢?
别看妹子啦,先看第一个问题,从实际的使用过程中来说,onStart和onResume、onPause和onStop看起来确实差不多。但Android为什么要提供看似重复的接口呢?根据上面的分析我们可以知道,这两对回调具有不同的意义,onStart和onStop是根据应用是否可见来进行回调的 ,onResume和onPause是根据应用是否位于前台来进行回调的,除此之外,无其他明显区别。
第二个问题,我们可以从Android源码里得到解释,A的onPause执行后B的onResume才会被调用。从另一个角度来说Android的官方文档中对onPause有这样一句解释:不能在onPause里进行重量级操作,因为必须在onPause执行过后,新的Activity才能Resume。
异常情况下的生命周期
onSaveInstanceState方法只会出现在 Activity被异常终止的情况下,它的调用时机是在 onStop之前,它和onPause方法没有既定的时序关系,可能在它之前,也可能在它之后。当 Activity被重新创建的时候, onRestoreInstanceState会被回调,它的调用时机是 onStart之后。系统只会在 Activity即将被销毁并且有机会重新显示的情况下才会去调用 onSaveInstanceState方法。当 Activity在异常情况下需要重新创建时,系统会默认为我们保存当前 Activity的视图结构,并且在 Activity重启后为我们恢复这些数据,比如文本框中用户输入的数据、listview滚动的位置等,这些 view相关的状态系统都会默认为我们恢复。具体针对某一个 view系统能为我们恢复哪些数据可以查看 view的源码中的onSaveInstanceState和 onRestoreInstanceState方法。
Demo
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
KLog.d(getClass().getSimpleName(),"onSaveInstanceState");
outState.putString(STATE, "test");
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
KLog.d(getClass().getSimpleName(),"[onRestoreInstanceState]: "
+ savedInstanceState.getString(STATE));
}
当我们旋转屏幕过后,可以看到如下的log:
10-23 22:50:56.032 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onCreate
10-23 22:50:56.036 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onStart
10-23 22:50:56.040 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onResume
10-23 22:51:05.456 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onPause
10-23 22:51:05.460 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onSaveInstanceState
10-23 22:51:05.460 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onStop
10-23 22:51:05.460 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onDestroy
10-23 22:51:05.484 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onCreate
10-23 22:51:05.496 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onStart
10-23 22:51:05.496 1613-1613/com.hugo.demo.activitydemo D/MainActivity: [onRestoreInstanceState]: test
10-23 22:51:05.496 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onResume
在onSaveInstanceState里面保存的test,确实在重新创建活动的时候在onRestoreInstanceState被还原出来了。
Android开发艺术探索上有这样一句话:
关于保存和恢复 View 的层次结构,系统工作流程是: Activity 异常终止, Activity 调用 onSaveInstanceState 去保存数据,然后 Activity 会委托 Windows 去保存数据,接着 Window 再委托它上面的顶层容器去保存数据。顶层容器是一个 ViewGroup ,一般来说它很可能是 DectorView ,最后顶层容器再去通知它的子元素保存数据。(这是一种委托思想,上层委托下层,父容器委托子元素去处理事情,如 View 的绘制过程,事件分发都是采用类似的思想)
Fragment的生命周期
普通的Fragment
从图中可以看出fragment的生命周期大部分和activity一致,不同的是:
- onAttach() :当Fragment 和 Activity产生关联时调用.
- onCreateView():为Fragment加载布局的时候调用
- onActivityCreated():当 Activity 的 onCreated() 方法返回后调用此方法
- onDestroyView():当 Fragment 中的视图被移除的时候,调用这个方法。
- onDetach():当 Fragment 和 Activity 分离的时候,调用这个方法。
除了上面的这些回调函数,fragment还有三个回调是值得我们注意的:
- onViewCreated():它在onCreateView()执行之后立马被调用,但它执行的时候,之前保存的状态还未被被恢复到了视图
- onSaveInstanceState():可在此保存fragment的一些数据,与activity不同的是,它可能在onDestroy之前的任何时间调用
- onViewStateRestored():在onActivityCreated()之后,onStart()之前被调用,以前在onSaveInstanceState()里保存的数据可以在这里还原。
ViewPager 中的 Fragment
我们开发中经常会用到 ViewPager + Fragment 组合的形式来完成特定的需求。本身 Fragment 生命周期就比 Activity 要复杂很多,当它在 ViewPager 中又是怎么回调呢?
我先给 ViewPager 加入三个 Fragment:
viewPager = (ViewPager) findViewById(R.id.viewpager);
fragmentList.add(new OneTextFragment());
fragmentList.add(new TwoTextFragment());
fragmentList.add(new ThreeTextFragment());
viewPager.setAdapter(new FtAdapter(getSupportFragmentManager(), fragmentList));
启动这个activity后,出现如下log:
10-23 23:17:19.332 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onCreate
10-23 23:17:19.388 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onStart
10-23 23:17:19.392 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onResume
10-23 23:17:19.404 1613-1613/com.hugo.demo.activitydemo D/OneTextFragment: onAttach
10-23 23:17:19.404 1613-1613/com.hugo.demo.activitydemo D/OneTextFragment: onCreate
10-23 23:17:19.408 1613-1613/com.hugo.demo.activitydemo D/OneTextFragment: onActivityCreated
10-23 23:17:19.408 1613-1613/com.hugo.demo.activitydemo D/OneTextFragment: onStart
10-23 23:17:19.412 1613-1613/com.hugo.demo.activitydemo D/OneTextFragment: onResume
10-23 23:17:19.412 1613-1613/com.hugo.demo.activitydemo D/TwoTextFragment: onAttach
10-23 23:17:19.412 1613-1613/com.hugo.demo.activitydemo D/TwoTextFragment: onCreate
10-23 23:17:19.412 1613-1613/com.hugo.demo.activitydemo D/TwoTextFragment: onActivityCreated
10-23 23:17:19.416 1613-1613/com.hugo.demo.activitydemo D/TwoTextFragment: onStart
10-23 23:17:19.416 1613-1613/com.hugo.demo.activitydemo D/TwoTextFragment: onResume
当活动进入到后台的时候:
10-23 23:19:37.536 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onPause
10-23 23:19:38.308 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onSaveInstanceState
10-23 23:19:38.308 1613-1613/com.hugo.demo.activitydemo D/OneTextFragment: onStop
10-23 23:19:38.312 1613-1613/com.hugo.demo.activitydemo D/TwoTextFragment: onStop
10-23 23:19:38.312 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onStop
当活动返回前台的时候:
10-23 23:28:19.568 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onRestart
10-23 23:28:19.568 1613-1613/com.hugo.demo.activitydemo D/OneTextFragment: onStart
10-23 23:28:19.568 1613-1613/com.hugo.demo.activitydemo D/TwoTextFragment: onStart
10-23 23:28:19.572 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onStart
10-23 23:28:19.572 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onResume
10-23 23:28:19.576 1613-1613/com.hugo.demo.activitydemo D/OneTextFragment: onResume
10-23 23:28:19.576 1613-1613/com.hugo.demo.activitydemo D/TwoTextFragment: onResume
当我滑动一页的时候:
10-23 23:30:26.560 30297-30297/com.hugo.demo.activitydemo D/ThreeTextFragment: onAttach
10-23 23:30:26.564 30297-30297/com.hugo.demo.activitydemo D/ThreeTextFragment: onCreate
10-23 23:30:26.568 30297-30297/com.hugo.demo.activitydemo D/ThreeTextFragment: onActivityCreated
10-23 23:30:26.568 30297-30297/com.hugo.demo.activitydemo D/ThreeTextFragment: onStart
10-23 23:30:26.572 30297-30297/com.hugo.demo.activitydemo D/ThreeTextFragment: onResume
可以看到在fragment进行滑动切换的时候,对下一个fragment进行了预加载。
再滑动一页:
10-23 23:32:29.612 30297-30297/com.hugo.demo.activitydemo D/OneTextFragment: onStop
10-23 23:32:29.612 30297-30297/com.hugo.demo.activitydemo D/OneTextFragment: onDestroyView
这个时候第一个fragment已经被销毁了。
当我们增加一行代码:viewPager.setOffscreenPageLimit(int limit)
10-23 23:38:47.236 4139-4139/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onCreate
10-23 23:38:47.272 4139-4139/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onStart
10-23 23:38:47.276 4139-4139/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onResume
10-23 23:38:47.280 4139-4139/com.hugo.demo.activitydemo D/OneTextFragment: onAttach
10-23 23:38:47.284 4139-4139/com.hugo.demo.activitydemo D/OneTextFragment: onCreate
10-23 23:38:47.284 4139-4139/com.hugo.demo.activitydemo D/OneTextFragment: onActivityCreated
10-23 23:38:47.288 4139-4139/com.hugo.demo.activitydemo D/OneTextFragment: onStart
10-23 23:38:47.288 4139-4139/com.hugo.demo.activitydemo D/OneTextFragment: onResume
10-23 23:38:47.292 4139-4139/com.hugo.demo.activitydemo D/TwoTextFragment: onAttach
10-23 23:38:47.292 4139-4139/com.hugo.demo.activitydemo D/TwoTextFragment: onCreate
10-23 23:38:47.292 4139-4139/com.hugo.demo.activitydemo D/TwoTextFragment: onActivityCreated
10-23 23:38:47.296 4139-4139/com.hugo.demo.activitydemo D/ThreeTextFragment: onAttach
10-23 23:38:47.296 4139-4139/com.hugo.demo.activitydemo D/ThreeTextFragment: onCreate
10-23 23:38:47.300 4139-4139/com.hugo.demo.activitydemo D/ThreeTextFragment: onActivityCreated
10-23 23:38:47.300 4139-4139/com.hugo.demo.activitydemo D/TwoTextFragment: onStart
10-23 23:38:47.304 4139-4139/com.hugo.demo.activitydemo D/TwoTextFragment: onResume
10-23 23:38:47.304 4139-4139/com.hugo.demo.activitydemo D/ThreeTextFragment: onStart
10-23 23:38:47.308 4139-4139/com.hugo.demo.activitydemo D/ThreeTextFragment: onResume
可以看到除了本来要显示的那个fragment外,还有其的2个fragment也被创建了出来。所以说我们通过viewPager.setOffscreenPageLimit(int limit)可以设置缓存fragment的个数。
Android的启动模式
Activity 的四种启动模式
- standard:标准模式,每次启动一个Activity都会产生一个新的实例,不论这个实例是否存在。
- singleTop: 栈顶复用模式,在这种情况下,如果新的Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时它的onNewIntent方法会被调用,通过此方法我们可以取出当前请求的信息。
- singleTask:栈内复用模式,这是一种单例模式,在这种模式下,只要有一个activity在一个栈中存在,那么多次启动这个activity都不会创建新的实例,和singleTop一样,系统也会回调其onNewIntent方法。注意:由于singleTask默认具有clearTop的效果,所以会导致所有在该活动上的其他activity全部出栈。如现在栈内的情况是ABCD,如果B以singleTask模式启动,启动B后,B上面的所有活动都会出栈,最后栈内的情况是AB。
- singleInstance:单实例模式,这是一种加强的singleTask实例,它除了具有singleTask模式的所有特性外,还加强了一点,那就是具有此种模式的activity只能单独的位于一个任务栈中。
这些启动模式可以在功能清单AndroidManifest.xml 中设置launchMode属性。
实际操作
-
singleTop:我们在清单里设置oneActivity的launchMode为singleTop,然后代码里设置活动的启动顺序为one-->one,反复点击多次,查看活动栈里的情况如下。
执行adb命令:adb shell dumpsys activity activitys
Running activities (most recent first):
Run #1: ActivityRecord{23e3b5b u0 com.hugo.demo.activitydemo/.dLaunchChapter.OneActivity t595}
Run #0: ActivityRecord{1a2c6f3 u0 com.hugo.demo.activitydemo/.LaunchActivity t595}
我们可以看到活动栈中只有一个one,说明oneActivity并没有被重新创建
-
singleTask:如果我们将oneActivity的launchMode设置为singleTask,然后在代码里设置活动的启动顺序为one->Two->one。
one -> Two 我们记录下当前activity的栈
我们记录下当前的 Activity 栈:
Running activities (most recent first):
Run #2: ActivityRecord{1e8701b7 u0 com.hugo.demo.activitydemo/.dLaunchChapter.TwoActivity t632}
Run #1: ActivityRecord{39e11719 u0 com.hugo.demo.activitydemo/.dLaunchChapter.OneActivity t632}
two->one 记录下当前的 Activity 栈:
Running activities (most recent first):
Run #1: ActivityRecord{39e11719 u0 com.hugo.demo.activitydemo/.dLaunchChapter.OneActivity t632}
可以看见,oneActivity上面的TwoActivity被出栈了,而oneActivity和上次内存信息相同,说明确实是复用了,没有创建新的实例。
TaskAffinity-任务相关性和allowTaskReparenting
这个参数标识了一个Activity所需要的任务栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名。当然,我们可以为每个activity单独指定TaskAffinity属性,这个属性必须和包名不一致,否则相当于没有指定。TaskAffinity属性主要和singleTask或allowTaskReparenting属性配对使用,在其他情况下没有意义。
举例:当应用A启动了应用B的一个活动C,然后按home键回到桌面,再在这个时候打开应用B,如果活动C的allowTaskReparenting属性值为true的话,那么当B被启动的时候,活动C会从应用A的任务栈移到应用B的任务栈中。可以这么理解,由于A启动了C,那么C肯定是在A的任务栈中的,但C属于B应用,那么C的TaskAffinity的值肯定不会和A的任务栈相同(因为包名不同)。所以当B被启动过后,B会创建自己的任务栈。系统在这个时候发现C所需要的任务栈已经被创建了,所以就把C从A的任务栈移到了B的任务栈。
提问:现在我们有3个活动,我们将oneActivity的launchMode属性设置为standard,twoActivity和threeActivity的launchMode属性设置为singleTask,并指定他们的taskAffinity属性为:"com.ryg.task1",注意taskAffinity属性的值为字符串。然后做如下操作。在oneActivity里面启动twoActivity,在twoActivity里面启动threeActivity,在threeActivity里面启动oneActivity,最后在oneActivity里面启动twoActivity,那现在按back键,显示的是哪个activity?
答案是返回到oneActivity。如此时再次按back键,将返回到桌面。
参考文章及Demo源代码
Android开发艺术探索第一章
Android 基础 -- 生命周期和启动模式实践总结
查看源码:Github,源代码来自@谢三弟