[Android]《Android开发艺术探索》学习笔记-Activity的生命周期和启动模式

一、典型情况下的生命周期分析

1、onCreate:表示Activity正在被创建,这是生命周期的第一个方法,可以做一些初始化工作。

2、onRestart:表示Activity正在重新启动,当当前的Activity从不可见重新变为可见时,这个方法就会被调用,比如用户按下Home键或者用户打开一个新的Activity,当前的Activity就会暂停,就是onPause()和onStop()被执行了,接着用户又回到了这个Activity,onRestart方法就会被调用。

3、onStart:表示Activity正在启动,即将开始,这时Activity已经可见,但还没在前台,还不能与用户交互,可以理解为Activity已经显示出来但我们还看不见。

4、onResume:表示Activity已经可见,并且已经出现在前台了,用户可交互了。

5、onPause:表示Activity正在停止,此时可以做一些存储数据、停止动画等工作,但注意不能太耗时,因为这会影响新的Activity,只有onPause执行完新的Activity的onResume才能执行

6、onStop:表示Activity即将停止,不可见了,可以做一些稍微重量级的回收,但也不能太耗时。

7、onDestroy:表示Activity即将被销毁,这是Activity生命周期的最后一个回调,在这可以做资源释放工作。

附加说明:

-针对一个特定的Activity,第一次启动:onCreate->onStart->onResume

-当用户打开新的Activity或按下Home键:onPause->onStop,有特殊,当Activity用了透明的主题,那么当前Activity不会回调onStop。

-当用户再次回到原Activity:onRestart->onStart->onResume。

-按返回键回退:onPause->onStop->onDestroy。

-被系统回收后再次打开:onCreate->onStart->onResume,不代表所有过程一样。

-onCreate和onDestroy只执行一次,而onStart/onStop、onResume/onPause是配对的可以调用多次。

 

二、异常情况下的生命周期分析

1、资源相关的系统配置发生改变导致Activity被杀死并重新创建。

如果我们的Activity不做特殊处理,那么当系统配置改变后Activity就会被销毁并重新创建,由于Activity是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态。这个方法只会出现在Activity被异常终止的情况下,正常情况不会回调。当Activity被重新创建后,系统会调用onRestoreInstanceState,并且把Activity销毁时onSaveInstanceState方法所保存的Bundle对象作为参数同时传给onRestoreInstanceState和onCreate方法,所以我们可以通过这两个方法判断Activity有没有被重新创建,从时许上来说onRestoreInstanceState在onStart之后。其实onSaveInstanceState和onRestoreInstanceState都已经为我们自动恢复了一些View相关的状态。关于保存和恢复View的层次结构,系统首先Activity会调用onSaveInstanceState去保存数据,然后Activity会委托Window去保存数据,接着Window会委托它上面的顶级容器去保存数据,顶层容器一般是DecorView;最后顶层容器再去一一通知它的子元素保存数据,这样整个数据的保存过程就完成了。

2、资源内存不足导致低优先级的Activity被杀死。

Activity按照优先级从高到低:前台Activity、可见但非前台Activity、后台Activity(已执行onStop)。当系统内存资源不足时会按优先级去杀死目标Activity所在进程,并在后续通过onSaveInstanceState和onRestoreInstanceState去恢复数据。一些后台工作不适合脱离四大组件而独自运行在后台中,所以我们最好将后台工作放入Service中保证进程有一定优先级,不会被轻易杀死。我们可以配置configChanges来让Activity不被重新创建,配置了configChanges会有个onConfigChanged方法让我们做一些特殊处理。

 

三、Activity的启动模式

1、Activity的LaunchMode

任务栈是一种“后进先出”的栈结构,他们的LaunchMode有以下四个模式:

(1)standard:标准模式,也是默认模式。每次启动都会创建一个新的实例进入到任务栈中。但用ApplicationContext启动standard的Activity时会报错,这是因为standard模式的Activity会默认进入启动它的Activity所属的任务栈中,但是非Activity类型的Context并没有所谓的任务栈,所以就有问题了。解决此问题可以为待启动Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就会为它创建一个新的把任务战,其实这时启动方式已经是singleTask模式了。

(2)singleTop:栈顶复用模式。如果新Activity已经在栈顶,那么此Activity不会被创建,而是调用此Activity的onNewIntent方法,此时此Activity的onCreate、onStart、onResume不会调用。如果存在于栈内而不在栈顶则会新建。

(3)singleTask:栈内复用模式。这是一种单例模式,只要任务栈内有该Activity,那么就不会新建实例,会回调onNewIntent方法。当一个具有singleTask模式的Activity请求启动后,首先寻找是否存在其想要的任务栈,如果不存在就新建任务栈,然后把Activity放入栈中,如果存在那么就看这个任务栈有没有这个Activity的实例,如果有这个Activity的实例就调用其onNewIntent方法,由于singleTask默认拥有clearTop的效果,所以会把这个Activity之上的Activity出栈。这里有个关键的参数:TaskAffinity,它可以指定任务栈,只在singleTask和allowTaskReparenting属性配对使用,其他情况没有意义。任务栈分前台任务栈和后台任务栈,可以切换后台任务栈到前台。当应用A启动了应用B的某个Activity后,如果这个Activity的allowTaskReparenting属性为true的话,那么当应用B被启动后,此Activity会直接从应用A的任务栈转移到应用B的任务栈中。

(4)singleInstance:单实例模式。它有singleTask的特性,还加强了一点,就是在singleInstance模式下的Activity会单独存在于一个新建的任务栈中

我们可以通过两种方式设置启动模式,一种是在AndroidMenifest来指定,一种是在代码里通过addFlag设置标记位。在代码里设置优先级高于在AndroidMenifest来指定。第一种方式无法直接为Activity设置FLAG_ACTIVITY_CLEAR_TOP标记,而第二种方式无法为Activity指定singleInstance模式。

2、Activity的Flags

FLAG_ACTIVITY_NEW_TASK:为Activity指定singleTask模式。

FLAG_ACTIVITY_SINGLE_TOP:为Activity指定singleTop模式。

FLAG_ACTIVITY_CLEAR_TOP:此标记位的Activity,启动时清空任务栈内在它之上的Activity,如果是standard模式启动, 那么会达到singleTask的效果。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:这个标记的Activity不会出现在历史Activity的列表中。

 

三、IntentFilter的匹配规则

启动Activity分为两种,显式调用(明确地指定被启动对象的组件信息,包括包名和类名)和隐式调用(不需要明确指定组件信息,需要Intent能匹配上目标组件的IntentFilter中所设置的过滤信息)。IntentFilter的过滤信息有action、category、data。为了匹配过滤列表,需同时匹配过滤列表中的action、category、data信息,否则匹配失败;一个Activity中可以有多个intent-filter,一个Intent只要能匹配任何一组intent-filter即可成功启动对应的Activity。

1、action的匹配规则

action匹配规则必须在Intent中的action存在并且和过滤规则中的其中一个action相同,它和category匹配规则不同,Intent如果没有指定,那么匹配失败。action区分大小写,需要字符串一模一样。

2、category的匹配规则

category是一个字符串,系统定义了一些category,我们也可以自定义category。Intent可以没有category,如果有,不管有几个,每个都要能和过滤规则中的任何一个category相同,Intent如果没有指定,也能匹配成功,因为在startActivity等方法中,系统默认给Intent加上了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:mimeType="string"

包含两部分:URI和mimeType。mimeType是指媒体类型,比如image/jpeg、audio/mpeg4-generic等,可以表示图片、文本、视频等不同的媒体格式,而URI中包含的数据就比较多了,下面是URI的结构:

<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
//实际例子
content://com.example.project:200/folder/subfolder/etc
http://www.baidu.com:80/search/info

Scheme:URI的模式,比如http、file、content等,如果URI中没有指定scheme,那么整个URI的其他参数无效,这也意味着URI是无效的。

Host:URI的主机名,比如www.baidu.com,如果host未指定,那么整个URI中的其他参数无效,这也意味着URI是无效的。

Port:URI中的端口号,比如80,仅当URI中指定了scheme和host参数的时候port参数才是有意义的。
Path、pathPattern和pathPrefix:这三个参数都是表示路径信息;其中path表示完整的路径信息;pathPattern也表示完整路径信息,但是它里面可以包含通配符“*”,“*”表示0个或多个任意字符;pathPrefix表示路径的前缀信息。

如果要为Intent指定完整的data,必须调用setDataAndType方法,而不能用setData和setType,因为这两个方法会覆盖对方。


当我们用隐式启动Activity的时候,可以用PackageManager的resolveActivity方法或者Intent的resolveActivity方法判断一下,如果找不到匹配的Activity就会返回null,防止android.content.ActivityNotFoundException异常。PackageManager还提供了queryIntentActivities(Intent intent,int flags)方法,返回的是所有成功匹配的Activity,第二个参数用MATCH_DEFAULT_ONLY保证只有android.intent.category.DEFAULT匹配规则的会返回,是因为如果没有这个匹配规则是无法接受隐式Intent的。

发表评论

电子邮件地址不会被公开。 必填项已用*标注