Activity生命周期、启动模式及启动方式
一. 正常的生命周期
activity生命周期.jpg
相关提问:
- onStart和onResume、onPause和onStop从描述上看差不多,对我们来说有什么实质不同?
答:在使用过程中,onStart和onResume、onPause和onStop看起来的确差不多,我们一般都只保留其中一对。Android系统之所以提供这些看起来重复的接口,是因为这两个配对的回调分别表示不同的意义。onStart和onStop是从Activity是否可见这个角度来回调的,而onResume和onPause是从Activity是否位于前台这个角度回调的。除了这种区别,在实际使用中没有其他明显区别。
onStart:表示Activity正在被启动,此时Activity已经可见但是还没有出现在前台,还在后台,不能和用户进行交互。可以理解为Activity已经显示出来了,但是我们还看不到;
onResume:表示Activity已经可见并且出现在前台并开始活动;
它两都表示Activity已经可见,但onStart的时候Activity还在后台,onResume的时候Activity才显示到前台。
- FirstActivity -> SecondActivity,那么SecondActivity的onResume和FirstActivity的onPause哪个先执行?
答:当新启动一个Activity的时候,旧Activity的onPause会先执行,然后才会启动新的Activity。onPause和onStop中都不能进行耗时操作,尤其是onPause,所以我们应当尽量在onStop中做操作,使新Activity尽快显示出来并切换到前台。如果新启动的Activity是透明主题,那么当前Activity不会回调onStop。
FirstActivity:onPause
SecondActivity:onCreate
SecondActivity:onStart
SecondActivity:onResume
FirstActivity:onStop
二. 异常情况下的生命周期
异常情况下Activity重建过程.png
异常情况分类:
- 资源相关的系统配置发生改变(比如横竖屏切换且不做处理)导致Activity被杀死并重新创建
- 资源内存不足导致低优先级的Activity被杀死
注意:
- 由于Activity是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态。当Activity被重新创建后,可以从onRestoreInstanceState和onCreate中取出之前保存的数据进行恢复。
- 关于保存和恢复View层次结构,系统的工作流程如下:首先Activity被意外终止时,Activity会调用onSaveInstanceState去保存数据,然后Activity委托Window去保存数据,接着Window再委托它上面的顶级容器去保存数据。最后顶层容器再去一一通知它的子元素来保存数据。
onSaveInstanceState方法的调用时机:在onStop之前,和onPause没有既定的时序关系。这个方法只会出现在Activity异常终止的情况下,正常情况不会回调该方法。
onRestoreInstanceState方法的调用时机:在onStart之后
三. 四种启动模式
-
standard:标准模式,也是系统的默认模式。每次启动一个Activity都会创建一个新的实例,不管这个实例是否已经存在。在这种模式下,谁启动了这个Activity,这个Activity就运行在启动它的那个Activity所在的栈中。
注意:当用ApplicationContext去启动这个模式的Activity的时候,会报错AndroidRuntimeException,因为非Activity类型的Context并没有所谓的任务栈。解决方法是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就会为它创建一个新的任务栈,这个时候待启动Activity实际上是以singleTask模式启动的。 -
singleTop:栈顶复用模式。在这种模式下,如果新Activity已经位于任务栈的栈顶位置,那么此Activity不会被重新创建,同时它的onNewIntent方法会被回调,通过此方法的参数我们可以取出当前请求的信息。
注意:上述情况下,onCreate、onStart不会被调用。 - singleTask:栈内复用模式。在这种模式下,只要Activity在一个栈中存在,那么多次启动该Activity都不会重新创建实例,系统也会回调其onNewIntent方法。
- singleInstance:单实例模式。他除了具有singleTask模式的所有特性外,该模式下的Activity只能单独位于一个任务栈中。解决了共享Activity实例的问题。
TaskAffinity参数:这个参数标识了一个Activity所需要的任务栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名。该属性主要和singleTask启动模式或者allowTaskReparenting属性配对使用,在其他情况下没有意义。
当TaskAffinity和allowTaskReparenting结合的时候,情况比较复杂。当一个应用A启动了应用B的某个Activity后,如果这个Activity的allowTaskReparenting属性为true,那么当应用B被启动后,此Activity会直接从应用A的任务栈转移到应用B的任务栈中。
指定启动模式的方式:
- AndroidMenifest中指定Activity的launchMode。不能直接为Activity设定FLAG_ACTIVITY_CLEAR_TOP标识。
- 在Intent中设置标记位,这种方式优先级高,两种方式同时存在时,以这种方式为主。无法为Activity指定singleInstance模式。
四. 启动Activity的方式
- 显式Intent
- 隐式Intent
隐式调用需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息,如果不匹配,将无法启动目标组件。IntentFilter中的过滤信息有action、category、data。一个Activity中可以有多个IntentFilter,一个IntentFilter中的action、category、data可以有多个,只有一个Intent同时匹配任何一组IntentFilter中的action、category、data类别才算完全匹配,只有完全匹配才能启动目标组件。
- action匹配规则:区分大小写,Intent中有一个action和过滤规则中的任何一个action相同即可匹配成功;
- category匹配规则:Intent中的所有category都必须和过滤规则中的一个category相同。为了activity可以接受隐式Intent,就必须在intent-filter中指定"android.intent.category.DEFAULT"这个category,因为系统调用startActivity或startActivityForResult的时候会默认为Intent加上"android.intent.category.DEFAULT"这个category。
- data匹配规则:由两部分组成,mimeType和URI。匹配规则和action类似。
注意:
-
当Manifest中有多个Activity与Intent所指定的匹配时,App会弹框出来让用户选择打开方式,每个Activity对应一个App的logo和名称,并没有Activity的相关信息。
-
为了避免找不到匹配的Activity而报错,可以先判断一下是否有可匹配的Activity。判断方法有如下几种,如果找不到匹配的Activity就会返回null。
- PackageManager的resolveActivity方法:返回最佳匹配的Activity信息
- PackageManager的queryIntentActivities方法:返回所有匹配的Activity信息
- Intent的resolveActivityInfo方法,也是内部调用PackageManager的resolveActivity方法
- Intent的resolveActivity方法,也是内部调用PackageManager的resolveActivity方法
上述方法中,第二个参数需要注意,我们一般使用PackageManager.MATCH_DEFAULT_ONLY这个标记位,这个标记位的含义是仅仅匹配那些在intent-filter中声明了"android.intent.category.DEFAULT"这个category的Activity。这个标记位的意义在于,只要方法不返回null,就一定可以成功启动新Activity。原因在category匹配规则中讲过。
五. 常用标记位
- FALG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有这个标记位的Activity不会出现在历史Activity的列表中。相当于xml中指定Activity的属性android:excludeFromRecents="true"。