一、Activity的生命周期全面分析
注意:
1、情况 1:资源相关的系统配置发生改变导致Activity被杀死并重新创建
onSaveInstanceState方法在onStop之前;
onRestoreInstanceState在onStart之后;
和onPause没有既定的时序关系;
当系统配置发生改变后,Activity会被销毁,其onPause、onStop、onDestory均会被调用,同时由于Activity是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态。这个方法的调用时机是在onStop之前,它和onPause没有既定的时序关系,它既可能在onPause之前调用,也可能在onPause之后调用。需要强调的一点是,这个方法只会出现在Activity被异常终止的情况下,正常情况下系统不会回调这个方法。如果被重建了,那么我们就可以取出之前保存的数据并恢复,从时序上来说,onRestoreInstanceState的调用时机在onStart之后。
关于保存和恢复View层次结构,系统的工作流程是这样的:首先Activity被意外终止时,Activity会调用onSaveInstanceState去保存数据,然后Activity会委托Window去保存数据,接着Window再委托它上面的顶级容器去保存数据。顶层容器是一个ViewGroup,一般来说它很可能是DecorView。最后顶层容器再去一一通知它的子元素来保存数据,这样整个数据保存过程就完成了。可以发现,这是一种典型的委托思想,上层委托下层、父容器委托子元素去处理一些事情,这种思想在Android中有很多应用,比如View的绘制过程、事件分发等都是采用类似的思想;
onRestoreInstanceState或者onCreate,二者的区别是:onRestoreInstanceState一旦被调用,其参数Bundle savedInstanceState一定是有值的,我们不用额外地判断是否为空;但是onCreate不行,onCreate如果是正常启动的话,其参数Bundle savedInstanceState为null,所以必须要额外判断。这两个方法我们选择任意一个都可以进行数据恢复,但是官方文档的建议是采用onRestoreInstanceState去恢复数据。
当然可以指定系统配置防止Activity被重新创建
android:configChanges="orientation|keyboardHidden"
2、情况2:资源内存不足导致低优先级的Activity被杀死
等级由低到高清除
(1)前台Activity——正在和用户交互的Activity,优先级最高。
(2)可见但非前台Activity——比如Activity中弹出了一个对话框,导致Activity可见但是位于后台无法和用户直接交互。
(3)后台Activity——已经被暂停的Activity,比如执行了onStop,优先级最低。
3、onNewIntent方法执行时期
1、其他Activity启动存在的SingleTask, singleInstance的Activity
onNewIntent-》onRestart -》onStart -》onResume
2、本Activity启动存在的SingleTop,SingleTask, singleInstance的本Activity
onPause -》onNewIntent -》onResume
注意:在onNewIntent()里面设置setIntent()才能获取最新的数据;
4、onActivityResult方法执行时期
onActivityResult -》onStart -》onResume
如果跳转的Activity是android:launchMode="singleInstance"属性,Activity一定在新的任务(进程)中,和原来的Activity不在同一进程中,onActivityResult函数表现异常;onActivityResult在startActivityForResult执行的一瞬间就被调用了。
二、Activity的启动模式
standard:这个是android默认的Activity启动模式,每启动一个Activity都会被实例化一个Activity,并且新创建的Activity在堆栈中会在栈顶。
singleTop:如果当前要启动的Activity就是在栈顶的位置,那么此时就会复用该Activity,并且不会重走onCreate方法,会直接它的onNewIntent方法,如果不在栈顶,就跟standard一样的。如果当前activity已经在前台显示着,突然来了一条推送消息,此时不想让接收推送的消息的activity再次创建,那么此时正好可以用该启动模式,如果之前activity栈中是A-->B-->C如果点击了推动的消息还是A-->B--C,不过此时C是不会再次创建的,而是调用C的onNewIntent。而如果现在activity中栈是A-->C-->B,再次打开推送的消息,此时跟正常的启动C就没啥区别了,当前栈中就是A-->C-->B-->C了。
singleTask:该种情况下就比singleTop厉害了,不管在不在栈顶,在Activity的堆栈中永远保持一个。这种启动模式相对于singleTop而言是更加直接,比如之前activity栈中有A-->B-->C---D,再次打开了B的时候,在B上面的activity都会从activity栈中被移除。下面的acitivity还是不用管,所以此时栈中是A-->B,一般项目中主页面用到该启动模式。
singleInstance:该种情况就用得比较少了,主要是指在该activity永远只在一个单独的栈中。一旦该模式的activity的实例已经存在于某个栈中,任何应用在激活该activity时都会重用该栈中的实例,解决了多个task共享一个activity。其余的基本和上面的singleTask保持一致。
上面的各种启动模式主要是通过配置清单文件,常见还有在代码中设置flag也能实现上面的功能:
FLAG_ACTIVITY_CLEAR_TOP:这种启动的话,只能单纯地清空栈上面的acivity,而自己会重新被创建一次,如果当前栈中有A-->B-->C这几种情况,重新打开B之后,此时栈会变成了A-->B,但是此时B会被重新创建,不会走B的onNewIntent方法。这就是单独使用FLAG_ACTIVITY_CLEAR_TOP的用处,能清空栈上面的activity,但是自己会重新创建。
如果在上面的基础上再加上FLAG_ACTIVITY_SINGLE_TOP此时就不重新创建B了,也就直接走B的onNewIntent。它两者结合着使用就相当于上面的singleTask模式。如果只是单独的使用FLAG_ACTIVITY_SINGLE_TOP跟上面的singleTop就没啥区别了。
FLAG_ACTIVITY_CLEAR_TOP+FLAG_ACTIVITY_SINGLE_TOP=singleTask,此时要打开的activity不会被重建,只是走onNewIntent方法。
FLAG_ACTIVITY_SINGLE_TOP=singleTop
FLAG_ACTIVITY_NEW_TASK
在相同taskAffinity情况下:启动activity是没有任何作用的。
在不同taskAffinity情况下:如果启动不同栈中的activity已经存在了某一个栈中的activity,那么此时是启动不了该activity的,因为栈中已经存在了该activity;如果栈中不存在该要启动的activity,那么会启动该acvitity,并且将该activity放入该栈中。
FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP一起使用,并且要启动的activity的taskAffinity和当前activity的taskAffinity不一样才会和singleTask一样的效果,因为要启动的activity和原先的activity不在同一个taskAffinity中,所以能启动该activity,这个地方有点绕,写个简单的公式:
FLAG_ACTIVITY_NEW_TASK如果启动同一个不同taskAffinity的activity才会有效果。
FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP如果一起使用要开启的activity和现在的activity处于同一个taskAffinity,那么效果还是跟没加FLAG_ACTIVITY_NEW_TASK是一样的效果。
FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP启动和现在的activity不是同一个taskAffinity才会和singleTask一样的效果。
FLAG_ACTIVITY_CLEAR_TASK
在相同taskAffinity情况下:和FLAG_ACTIVITY_NEW_TASK一起使用,启动activity是没有任何作用的。
在不同taskAffinity情况下:和FLAG_ACTIVITY_NEW_TASK一起使用,如果要启动的activity不存在栈中,那么启动该acitivity,并且将该activity放入该栈中,如果该activity已经存在于该栈中,那么会把当前栈中的activity先移除掉,然后再将该activity放入新的栈中。
FLAG_ACTIVITY_NEW_TASK+FLAG_ACTIVITY_SINGLE_TOP 用在当app正在运行点击push消息进到某个activity中的时候,如果当前处于该activity,此时会触发activity的onNewIntent。
FLAG_ACTIVITY_NEW_TASK+FLAG_ACTIVITY_CLEAR_TOP用在app没在运行中,启动主页的activity,然后在相应的activity中做相应的activity跳转。
三、IntentFilter的匹配规则
Actvitiy分为两种启动模式,分别是显式调用和隐式调用。隐式调用需要指定IntentFilter,其中包含了action、category、data。只有三个同时匹配时(不需要三个必须同时存在,如果没有则默认匹配成功),才能算IntentFilter匹配成功。此外一个activity可以有多个IntentFilter,一个intent只需要匹配成功一个就算匹配成功,能够启动对应的activity。
1、action
action是一个字符串。一个IntentFilter可以有多个action,只需要匹配任意一个action,就算action项匹配成功。action的匹配规则是,intent中的action字符串必须和IntentFilter中的action的字符串值完全相同,并且区分大小写。
2、category
category也是一个字符串。系统预定义了一些category,也可以自定义category。一个IntentFilter中可以有多个category,但在匹配时,intent中添加的所有category必须匹配所有的category才能算匹配成功。此外如果intent不设置category,则有默认值default
3、data
data由两部分组成,分别是miniType和URI,miniType指的是媒体类型,比如image/jpeg、audio/mpeg4-generic和video/*。而uri的结构如下所示。
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
举几个例子
content://com.example.project:200/folder/subfolder/etc
http://www.baidu.comn:80/search/info
scheme:代表url的模式,比如http、file、content等,这是必填项
host:代表uri的主机名,如www.baidu.com,这也是必填项。
port:uri中的端口号,这是选填项。
path、pathPrefix、pathPattern:这三个参数表示路径的信息,其中path表示完整路径,pathPrefix表示带有路径的前缀信息,pathPattern代表有通配符“*”表达式来匹配路径。
匹配符号:
“*” 用来匹配0次或更多,如:“a” 可以匹配“a”、“aa”、“aaa”…
“.” 用来匹配任意字符,如:“.” 可以匹配“a”、“b”,“c”…
因此 “.” 就是用来匹配任意字符0次或更多,如:“.*html” 可以匹配 “abchtml”、“chtml”,“html”,“sdf.html”…
data的匹配规则
和action一样,只需要匹配manifest中任意一个即可
比如如下所示的代码,image/*代表匹配所有类型的图片,此时虽没有制定uri,但是有默认值content和file,也就是说intent中的data的schema必须为content或者file才能匹配。
<intent-filter>
……
<data android:mimeType="image/*"/>
</intent-filter>
注意:
1、注意当需要同时设置uri和type的时候,必须使用setDataAndType。如果分开调用setData和setType,都会先清除对方的值,在进行赋值。
intent.setDataAndType(Uri.parse("file://abc"),"image/*");
2、uri和url有什么区别?
uri是为了能唯一标识某个资源,url则是通过地址的方式,标识某个资源,因此url是uri的子集,是一种具体的实现方式。
参考:Activity的知识
四、Fragment生命周期
1、Fragment 作用
Android 在 Android 3.0 (API 11)中引入了片段,主要是为了给更大的屏幕(如平板电脑)上更加动态和灵活的 UI 设计提供支持。由于平板屏幕比手机的屏幕大得多,也能显示更多的布局和组件。
2、Fragment生命周期
onAttach() 在Fragment 和 Activity 建立关联是调用(Activity 传递到此方法内)
onCreateView() 当Fragment 创建视图时调用
onActivityCreated() 在相关联的 Activity 的 onCreate() 方法已返回时调用。
onDestroyView() 当Fragment中的视图被移除时调用
onDetach() 当Fragment 和 Activity 取消关联时调用。
-> Activity.onAttach -> Fragment.onAttch
-> Fragment.onCreate -> Fragment.onCreateView
-> Activity.onCreate -> onActivityCreated ->
-> Activity.onStart -> Fragment.onStart
-> Activity.onResume -> Fragment.onResume
-> Fragment.onPause -> Activity.onPause
-> Fragment.onStop -> Activity.onStop
-> Fragment.onStop -> Activity.onStop
-> Fragment.onDestroyView -> Fragment.onDestroy
-> Fragment.onDetach -> Activity.onDestroy
3、Fragment事务添加Fragment
Fragment newFragment = new MainFragment();
FragmentTransaction mTransaction = getFragmentManager().beginTransaction();
//用新的 fragment 替换原来fragment 所在位置的布局,并且把此事务添加到返回栈中。
mTransaction.replace(R.id.frame_layout,newFragment);
mTransaction.addToBackStack(null);
mTransaction.commit();