Activity的生命周期和启动模式
1、Activity的生命周期解析
所谓的典型情况下的生命周期,是指用户参与的情况下,Activity所经过的生命周期的改变;而异常情况下的生命周期是指Activity被系统回收或者由于当前设备的Configuration发生改变从而导致Activity被销毁重建。
1.1、典型情况下
1)、针对一个特定的Activity,第一次启动,回调如下:onCreate()--->onStart()---->onResume()
2)、当用户 打开新的Activity或者切换到桌面时,回调如下:onPause()-->onStop(),特殊情况,如果新的Activity采用了透明的主题,那么当前Activity不会回调onStop
3)、当用户再次回到原Activity时,回调如下:onRestart()--->onStart()--->onResume()
4)、当用户按back键回退时,回调如下:onPause()--->onStop()--->onDestory()
问题:
1、onStart和onResume、onPause和onStop从描述上来看差不多,对我们来说有什么实质性的区别?
onStart和onStop是从Activity是否可见这个角度来回调的;onResume和onPause是从Activity是否位于前台这个角度来回调的,除了这种区别,在应用中并没有其他明显区别。
2、假设当前的Activity为A,如果这时用户打开一个新Activity B,那么B的onResume和A的onPause哪个先执行?
调用顺序为:A onPause()---->B onCreate()---->B onStart()---->B onResume()---->A onStop();因此,onPause和onStop都不能做耗时操作,尤其是onPause,这也意味着,我们应该尽量在onStop中做操作,从而使得新的Activity尽快显示出来并切换到前台。避免出现页面切换时的卡顿现象。
1.2、异常情况下
1)、资源相关的系统配置发生改变导致Activity被杀死并重新创建
在默认情况下,如果我们的Activity不做任何特殊处理,那么系统配置发生改变后,Activity就会销毁并重新创建。
当系统配置发生改变后,Activity会被销毁,其onPause、onStop、onDestroy均会被调用,同时由于Activity是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态。调用时机是在onStop之前,它和onPause没有既定的时序关系。
当Activity被重新创建后,系统会调用onRestoreInstanceState,并且把Activity销毁时onSaveInstanceState方法说保存的Bundle对象作为参数传给onRestoreInstanceState和onCreate方法。
当Activity在异常情况下需要重新创建时,系统会默认为我们保存当前Activity的视图结构,并且在Activity重启后为我们恢复这些数据。
2)、内存不足导致低优先级的Activity被杀死
Activity按照优先级从高到低,可以分为三种:
1)、前台Activity--正在和用户交互的Activity,优先级最高
2)、可见但非前台Activity--比如Activity中弹出一个对话框,导致Activity可见但无法与用户直接交互
3)、后台Activity--已经被暂停的Activity,不可见,优先级最低
如果一个进程中没有四大组件在执行,那么这个进程将很快被系统杀死。因此,一些后台工作不适合脱离四大组件而独立运行在后台中,把后台工作放入Service中,从而保证进程有一定的优先级,这样不会轻易被系统杀死。
如果我们不想系统重新创建Activity,那么可以给Activity指定configChanges属性。比如不想让Activity在屏幕旋转时重新创建就可以给configChanges属性添加orientation这个值。
<span style="white-space:pre"> </span>android:configChanges="orientation"
配置了以上属性,系统就不会在对应变化时重新创建,而是调用Activity的onConfigurationChanged方法。
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
2、Activity的启动模式
2.1、Activity的LaunchMode
Activity的启动模式有四种,分别为:
1)、standard:标准模式,这也是系统的默认模式。每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否存在。
standard模式的Activity默认会进入启动它的Activity所属的任务栈中,对于非Activity类型的Context(如ApplicationContext),并没有任务栈,启动Activity时需要加上
FLAG_ACTIVITY_NEW_TASK标记位,这样启动时就会为它创建一个新的任务栈。
2)、singleTop:栈顶复用模式,如果行Activity已经位于任务栈的栈顶,那么此Activity不会被创建,同时它的onNewIntent方法会被回调,通过此方法的参数我们可以取出当前请求的信息。如果新的Activity实例已存在但不是位于栈顶,那么新Activity仍然会重建。
3)、singleTask:栈内复用模式,栈内单例模式,在这种情况下,只要新Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,和SingleTop模式一样,系统也会回调onNewIntent。并且把新Activity移到栈顶,去除新Activity上面的其他Activity。
4)、singleInstance:单实例模式,这是一种加强的singleTask模式,它具有singleTask模式的所有特性,此种模式的Activity只能单独地位于一个任务栈中。
taskAffinity:任务相关性,它标识一个Activity说需要的任务栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名,taskAffinity属性主要和singleTask启动模式或者allowTaskReparenting属性配对使用,在其他情况下没有意义。
当taskAffinity和singleTask启动模式配对使用时,它是具有该模式的Activity的目前任务栈的名字,待启动的Activity会运行在名字和taskAffinity相同的任务栈中。
当taskAffinity和allowTaskReparenting结合时,这种情况比较复杂,会产生特殊的效果。当一个应用A启动了应用B的某个Activity,如果这个Activity的allowTaskReparenting属性为true的话,那么当应用B被启动后,此Activity会直接从应用A的任务栈转移到应用B的任务栈中。
给Activity指定启动模式的两种方式:
1)、通过AndroidMenifest为Activity指定启动模式
2)、通过Intent标识位来为Activity指定启动模式
区别:优先级上第二种方式高于第一种方式,当两种同时存在时,以第二种为准;
第一种方式无法直接为Activity设定FLAG_ACTIVITY_CLEAR_TOP标识,而第二种方式无法为Activity指定singleInstance模式
2.2、Activity的Flags
1)、FLAG_ACTIVITY_NEW_TASK:这个标识位的作用是为Activity指定“singleTask”启动模式,其效果和在xml中指定该启动模式相同
2)、FLAG_ACTIVITY_SINGLE_TOP:这个标识位的作用是为Activity指定“singleTop”启动模式,其效果和在XML中指定该启动模式相同
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";
3、IntentFilter的匹配规则
启动一个Activity分为显示调用和隐式调用
隐式调用需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息,如果不匹配将无法启动目标Activity。IntentFilter中的过滤信息有action、category、data。为了匹配过滤列表,需要同时匹配过滤列表中的action、category、data信息,否则匹配失败。
只有一个Intent同时匹配action类别、category类别、data类别才算完全匹配。只有完全匹配才能成功匹配目标Activity。一个Activity中可以有多个intent-filter,一个intent只要能匹配任何一组intent-filter即可成功启动对应的Activity。
1)、action匹配规则
action是一个字符串,系统预定义了一些action,同时我们也可以在应用中定义自己的action。action的匹配规则是Intent中的action必须能够和过滤规则中的action匹配,这里说的匹配是指action的字符串值完全一样。一个过滤规则中可以有多个action,那么只要Intent中的action能够和过滤规则中的任何一个action相同即可匹配成功。
2)category匹配规则
category是一个字符串,系统预定义了一些category,同时我们也可以在应用中定义自己的category。Intent中如果出现了category,不管有几个category,对于每个category来说,它必须是过滤规则中的定义的category。
category和action匹配过程不同,action是要求Intent中必须有一个action且必须能够和过滤规则中的某个action;而category要求Intent可以没有category,但如果一旦有category,不管有几个,每个都要能够和过滤规则中的任何一个category相同。
为什么不设置category也可以匹配呢?原因是系统在调用startActivity或者startActivityForResult时会默认为Intent加上“android.intent.category.DEFAULT”这个category。
为了我们的Activity能够接收隐式调用,就必须在intent-filter中指定“android.intent.category.DEFAULT”这个category,这样intent中默认不添加category也不会报错。
3)、data匹配规则
data的匹配规则和action类似,如果Intent-filter中定义了data,那么intent中必须也要定义可匹配的data。
data又两部分组成,mimeType和URI。mimeType指媒体类型,比如image/jpeg、audio/mpeg4-generic和video/*等,可以表示图片、文本、视频等不同的媒体格式,而URI中包含的数据就比较多了
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern]
Scheme:URI模式,比如:http、file、content等,必须指定Scheme。
Host:URI的主机名,比如:www.baidu.com,必须制定host。
Port:URI中的端口号,比如80,仅当URI中指定了scheme和host参数时port参数才是有意义的。
Path、pathPattern和pathPrefix:这三个参数表述路径信息。
如下过滤规则
<span style="white-space:pre"> </span><intent-filter>
<data android:mimeType="image/*" />
...
</intent-filter>
这种规则指定了媒体类型为所有类型的图片,那么Intent中的mimeType属性必须为“image/*”才能匹配,这种情况下虽然过滤规则没有指定URI,但有默认值,URI默认为content和file。也就是说,虽然没有指定URI,但是Intent中的URI部分的scheme必须为content或者file才能匹配,这点需要尤其注意的。
intent.setDataAndType(Uri.parse("file://adb"),"image/png");