1、Activity的生命周期的解析:
1、典型情况下的生命周期
2、异常情况下的生命周期
典型情况:是指在有用户参与的情况下,Activity所经过的生命周期的改变;
异常情况:是指Activity被系统回收或则由于当前设备的Configuration发生改变从而导致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已经可见了,并且出现在前台并开始活动。要注意这个和onStat的对比,onStart和onResume都表示Activity已经可见,但是onStart的时候Activity还在后台,onResume的时候Activity才显示到前台。
5、onPause:表示Activity正在停止,正常情况下,紧接着onStop就会被调用,在特殊情况下,如果这个时候快速再回到当前Activity,那么onResume会被调用
6、onStop:表示Activity即将停止,可以做一些稍微轻量级的回收工作,同样不能太耗时。
7、onDestroy:表示Activity即将被销毁,这是Activity生命周期中的最后一个回调,在这里,我们可以做一些回收工作和最终的资源释放。
从整个生命周期来说,onCreate和onDestroy是配对的,分别标识着Activity的创建和销毁,并且只可能有一次调用。从Activity是否可见来说,onStart和onStop是配对的,随着用户的操作或则设备屏幕的点亮和熄灭,这两个方法可能被调用多次;从Activity是否在前台来说,onResume和onPause是配对的,随着用户操作或则设备屏幕的点亮和熄灭这两个方法可能被调用多次。
问题引入:
1、onStart和onResume,onPause和onStop从描述上看差不多,对我们来说有什么实质的不同呢?
2、假设当前Activity为A,如果这时用户打开一个新ActivityB,那么B的onResume和A的onPause那个先执行呢?
解答问题:
1、从实际使用过程中,onStart和onResume、onPause和onStop看起来差不多,甚至我们可以只保留其中一对,比如只保留onStart和onStop。既然如此Android系统还要提供看起来重复的接口呢?根据上面的分析,我们知道,这两个配对的回调分别表示不同的意义,onStart和onStop是从Activity是否可见这个角度来回调的,而onResume和onPause是从Activity是否位于前台这个角度来回掉的,处理这种区别,在实际使用中没有其它明显区别。
2、在新Activity启动之前,栈顶的Activity需要先onPause后,新Activity才能启动
sample:当MainActivity跳转到Second Activity,生命周期的流程如下:
MainActivity onPause
SecondActivity onCreate
SecondActivity onStart
SecondActivity onResume
MainActivity onStop
2、异常情况下的生命周期:
1、情况:资源相关的系统配置发生改变导致Activity被杀死并重新创建
Activity是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态。这个方法的调用时机是在onStop之前,它和onPause没有既定的时序关系,他既可能在onPause之前调用,也可能在onPause之后调用。需要强调一点是,这个方法只会出现在Activity被异常终止的情况下。我们可以通过onRestoreInstanceState和onCreate方法来判断Activity是否被重建了,如果被重建了,那么我们就可以取出之前保存的数据并恢复,从时序上来说,onRestoreInstanceState的调用时机在onStart之后
在onSaveInstanceState和onRestoreInstanceState方法中,系统会默认为我们做一定的恢复工作【如:Activity的视图结构】
2、情况:资源内存不足导致优先级低的Activity被杀死
这中情况不好模拟,但是其数据存储和恢复过程和情况1完全一致,这里我们描述一下Activity优先级:
1、前台Activity---正在和用户交互的Activity,优先级最高。
2、可见但并非前台Activity----比如Activity中弹出了一个对话框,导致Activity可见但是位于后台无法和用户交互。
3、后台Activity-----已经被暂停的Activity,比如执行了onStop,优先级最低。
*****:如果不想让Activity在屏幕旋转的时候重新创建,就可以给configChanges属相添加orientaion这个指,如下所示:
android:configChanges="orientation"
需要说明的是:当编译时指定的minSdkVersion和targetSdkVersion有一个大于13,所以为了防止旋转屏幕时Activity重启,除了orientation,我们还要加上screenSize,
进行上面的操作后,当屏幕旋转,Activity并不会重新创建,取而代之的是系统调用了Activity的onConfigurationChanged方法,在这里我们可以做一些自己的特殊处理。
@override
public void onConfigurationChanged(Configuration newConfig){
super.onConfiguratinChanged(newConfig);
Log.d(TAG, "onConfigurationChanged, nwe Orientaion:" + newConfig.orientaion);
}
2、Activity的启动模式解析:
1、Activity的LaunchMode
1、singleInstance
2、standard
3、singleTop
4、singleTask
2、如何给Activity指定启动模式?
1、通过AndroidManifest为Activity指定启动模式
2、通过Intent设置标志位来为Activity指定启动模式,
sample:
Intent intent = new Intent();
intent .setClass(MainActivty.this,SecondActivity.class);
intent .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
两种方式的优先级:
通过代码设置flag的优先级要大于在Manifest中配置启动模式的优先级,
当两种方式都存在时,以第二种方式为准;
Activity的常用Flags:
1、FLAG_ACTIVITY_NEW_TASK:
效果和在Manifest文件指定“singleTask”一样。
2、FLAG_ACTIVITY_SINGLE_TOP
效果和在Manifest文件指定“singleTop”一样。
3、FLAG_ACTIVITY_CLEAR_TOP
具有此标志位的Activity,当他启动时,在同一个任务栈中所有位于它上面的Activity都要出栈,这个标记位一般会和singleTask启动模式一起出现,在这种情况下,被启动Activity的实例如果已经存在,那么系统就会调用它的onNewIntent。如果被启动的Activity采用的standard模式启动,那么它连同它之上的Activity都要出栈,系统会创建新的Activity实例并且放入栈顶,
4、FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
具有这个标志位的Activity不回出现在历史Activity的列表中,当某些情况下我们不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用。它等同于在xml中指定Activity的属性android:excludeFromRecents = "true"。【ps:本人经过测试:当这个属性设置给MainActivity时候,点击Home键,回到桌面,长按Home进入应用历史列表:发现当前的应用在应用历史列表中不存在了,(近在设置MainActivity属性才会出现这中问题,如果设置Second Activity属性,或则不是应用第一次启动的Activity,这种效果不明显,或则说不会出现上面说的:“当某些情况下我们不希望用户通过历史列表回到我们的Activity的时候”)】
1、IntentFilter的匹配规则
启动Activity分为两种,显式调用和隐式调用。
1、显式调用 :需要明确地指定被启动对象的组件信息,包括包名和类名,
2、隐式调用 :不需要明确指定组件信息。
原则上一个Intent不应该既是显示的又是隐式的,但是两者共存的话以显式调用为主。
【隐式调用】
IntentFilter中的过滤信息有action、category、data,
为了匹配过滤列表,需要同时匹配过滤列表中的action、category、data信息,否则匹配信息失败。
一个过滤列表中的action、category、data可以有多个,所有的action、category、data分别构成不同类别,同一类别的信息共同约束当前类别的匹配过程。只有一个Intent同时匹配action类别、category类别、data类别才算完全匹配,只有完全匹配才能成功启动目标Activity。另外一点,一个Activit中可以有多个intent-filter,一个Intent只要能匹配任何一组intent-filter即可成功启动对应的Activity。
1、action的匹配规则:
action是一个字符串,系统预定了一些action,同时我们也可以在应用中定义自己的action。action的匹配规则是Intent中的action必须能够和过滤规则中的action匹配,这里说的匹配是指action的字符串值完全一样。一个过滤规则中可以有多个action,那么只要Intent中的action能够和过滤规则中的任何一个action相同即可匹配成功。针对上面的过滤 规则,只要我们的action中个action值为intent-filter中的其中一个action值相同,就可匹配成功。【action区分大小写,大小写不同,字符串相同的action,会匹配失败:smaple:TAG和tag是两个不同的action】
2、category的匹配规则:
category是一个字符串,系统预定了一些category,同时我们也可以在应用中定义自己的category。category的匹配规则和action不同,它要求Intent中如何含有category,那么所有的category都必须和过滤规则中的其中一个category相同。换句话说,Intent中如果出现了category,不管有几个category,对于每个category来说,他必须是过滤规则中已经定义了的category。当然,Intent中可以没有category,如果没有category的话,按照上面的描述,这个Intent仍然可以匹配成功。这里要注意下它和action匹配过程的不同,action是要求Intent必须有一个action且必须能够和过滤规则中的某个action相同,为了匹配前面的过滤规则中的category,我么可以写出下面的Intent,也可以不设置category。当不设置category是,系统为默认设置category为“android.intent.category.DEFAULT”当然为了我们的activity能够接收隐式调用,就必须在intent-filter中指定“android.intent.category.DEFAULT”
3、data的匹配规则:
data的匹配规则和action类似,如果过滤规则中定义了data,那么Intent中必须也要定义可匹配的data.
data的语法:
<data android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mineType="string"/>
data有两部分组成,mimeType和URI。mimeType指媒体类型,比如image/jpeg、audio/mpeg4-generic和video/*等,可以表示图片、文本、视频等不通过的媒体格式,而URI中包含的数据就比较多了,
【每个数据含义】
1、Scheme:URI的模式,比如http、file、content等,如果URI中没有指定scheme,那么整个URI的其它参数无效,这也意味着URI是无效的。
2、Host:URI的主机名,比如www.baidu.com,如果host未指定,那么整个URI中的其它参数无效,这也意味着URI是无效的,
3、Port:URI中的端口号,比如80,仅当URI中个指定了scheme和host参数的时候port参数才是有意义的。
4、Path、PathPattern、PathPrefix:这三个参数表述路径信息,其中path表示完整的路径信息;pathPattern也表示完整的路径信息,但是它里面可以包含通配符”*“,”*“表示0个或多个任意字符,需要注意的是,由于正则表达式的规范,如果想表示真实的字符串,那么”*“要写成”\\*“,"\"要写成”\\\\“;pathPrefix表示路径的前缀信息。
最后,当我们通过隐式方式启动一个Activity时候,可以做一下判断,看是否有Activity能够匹配我们的隐士Intent,如果不判断就有可能 出现上述的错误。判断方法有两种:
1、采用PackageManager的resolveActivity方法
2、Intent的resolveActivity方法,
如果他们找不到匹配的Activity就会返回null,我们通过判断返回值就可以规避上述的错误。
另外:PackageManager还提供了queryIntentActivities方法,这个方法和resolveActivity方法不同的是:他不是返回最佳匹配的Activity信息而是返回所有成功匹配的Activity信息
publc abstract List<ResolveInfo> queryIntentActivities(Intent intent ,int flags);
publc abstract ResolveInfo resolveActivity(Intent intent ,int flags);
注意:第二个参数flags,我们要使用MATCH_DEFAULT_ONLY这个标记位,这个标记位的含义是仅仅匹配那些在intent-filter中声明了<category android:name="android.intent.category.DEFAULT"/>
这个category的Activity。使用这个标记位的意义在于,只要上述两个方法不返回null,那么startActivity一定可以成功。如果不用这个标记位,就可以把intent-filter中category不含DEFAULT这个category的Activity给匹配出来,从而导致startActivity可能失败。
因为不含有DEFAULT的Activity是无法接收隐式Intent的,
代码示例:
/**
* 判断是否可以成功的跳转
*/
List<ResolveInfo> resolveInfoList = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if(resolveInfoList == null || resolveInfoList.size() == 0){
//判断size==0是resolveInfoList 可能不为null,但会是空集合
Toast.makeText(this, "条件不允许哦", Toast.LENGTH_SHORT).show();
return;
}else{
startActivity(intent);
}
在action和category中,有一类action和category比较重要,它们是:
<action android:name = "android.intent.action.MAIN"/>
<category android:name = "android.intent.category.LAUNCHER"/>
这两者是用来标明这是一个入口Activity,并且会出现在系统的应用列表中,少了任何一个都没有实际意义,也无法出现在系统的应用列表中,也就是二者缺一不可。另外,针对Service、ContentProvide和Broadcast,PackageManager同样提供了类似的方法去获取成功匹配的组件信息。
2、
扩展补充:
问题:
1、
onSaveInstanceState方法在Activity的哪两个生命周期方法之间调用?
2、弹出一个Dialog时,onPause会调用吗?什么情况下会,什么情况下不会?
3、横竖屏切换的时候,生命周期方法是如何调用的?如何进行配置呢?
4、Activity调用了onDestory方法,就会在Activity的任务栈消失吗?
5、永久性质的数据,应该在哪个生命周期方法中保存?
6、在onCreate或者onRestoreInstance方法中恢复数据时,有什么区别?
解答:
1、onSaveInstanceState与onPause()调用的先后,存在不确定性,但是一定会在onStop();之前调用。
2、当在ActivityA弹出一个Dialog(此Dailog属于ActivityA)时,ActivityA的onPause()不会调用。
若:Dailog属于ActivityB,此时ActivityA的onPause()会被调用。
3、
onPause--onSaveInstanceState--onStop--onDestory--onCreate--onStart--onResume。
配置:
android:
configChanges=
"keyboardHidden|orientation|screenSize"
4、若是通过back键等调用,会从任务栈中消失;若是直接调用onDestroy();则不会消失。
5、onPause();适合存储轻量级的数据,尽量越少越好,因为当启动一个新的Activity时:若ActivityA启动ActivityB,ActivityB启动是在ActivityA的onPause()后的。
onStop();也可以存储。
6、onCreate方法中的Bundle数据需要判空处理;onRestoreInstance的Bundle不需要判空处理。