[Andorid开发艺术探索 读书笔记]Activity的生命周期和启动模式 (二)

这篇文章主要讨论Activity的启动模式,这是关于Activity很重要的一个知识。


我们知道,在默认情况下,我们启动一个activity的时候,系统会创建一个对应的activity的对象实例,并将其压入任务栈中,换句话说,系统会用一个栈的结构来管理activity的实例。既然是栈,那么就遵循着“先入后出”的基本原则。一个比较重要的信息是:默认情况下,也就是对这个activity没有特别的配置情况下,同一个activity每次启动都会生成一个新的实例,这样在多次一个activity的时候,会重复创建多个实例。此为背景,具体task栈的细节问题我们会在以后的博客中详细讨论。


android系统为我们提供了四种activity的启动模式:standard, singleTop, singleTask, singleInstance



简单来说,

   standard就是默认模式,我们没有特别配置的情况下,activity的启动就是这个模式,前面有提到的,同一个activity每次启动都会生成一个新的实例。

   singleTop栈顶复用模式,在这种模式下,如果你要启动的activity恰好位于任务栈的顶部,系统不会为这个activity创建新的实例,而是会去复用栈顶的这个实例对象

   singleTask栈内复用模式,在这种模式下,如果你要启动的activity在栈内如果存在,那么很好,栈内位于这个实例上面所有的activity实例都会被销毁,系统会复用这个activity实例

   singleInstance单实例模式,这种模式比较特别。他指如果一个activity被设置为singleInstance模式,当启动这个activity的时候,系统会另起一个task栈来管理这个activity实例,在这个task栈中有且仅有一个这个activity的实例。


有一点说明的是,这些启动模式的配置都可以在manifest.xml中,我们知道每个activity都需要在manifest中声明,我们可以在声明相应activity的时候配置lauchMode。

现在相信你对这四种启动模式有了一个初步的认识,让我们来对每种启动模式做一些详细的讨论。


standard : 这是标准的模式。每次启动一个activity,不管这个实例是否已经存在,被创建的实例的生命周期都符合典型的activity的生命周期,就像上篇博客里说的,它的onCreate,onStart,onResume都会被调用,这是一种典型的多实例实现。


对这种启动模式,任玉刚老师写了这样一句话:一个任务栈中可以有多个实例,每个实例也可以属于不同的任务栈。 这句话表示:如果activity A启动了activity B,那么B就会进入A所在的栈中。比如我们写的应用中有一个activity页面被别人应用中的activity启动,那么我们这个activity默认情况下会被压入到别人应用中的task栈。听起来有些尴尬,当然我们可以通过一些办法去避免这样的事情发生。


在实际的开放过程中,当我们去用ApplicationContext在standard模式下去启动一个activity的时候,系统会报错。原因这样看来就很明确了,因为启动一个activity会默认压入启动它的activity的task栈中,但是非activity类型的context(如这里的ApplicationContext)并没有所谓的task栈,所以这就有问题了。解决的方案其实也简单,就是在启动这个activity的intent里添加一个flag标记位:FLAG_ACTIVITY_NEW_TASK,这样它启动的时候,就会为它创建一个新的任务栈。


singleTop : 这种模式比较简单,但是值得提到的一点是:当一个activity 的实例已经位于栈顶了,一个intent去起这个activity,我们可以复写activity中的onNewIntent方法来接受这个intent进行相关的业务处理。为了说明singleTop与standard模式的区别,我们来考虑这样一个情景。现在task栈中有四个activity:ABCD,这时候要启动activity D,如果我们没有配置activity D的lauchMode,那么task栈就会变为ABCDD,但是如果activity D配置了lauchMode为singleTop的话,系统将不会新压入一个activity D的实例,task栈为ABCD。


singleTask : 这个模式相当于singleTop的加强版,singleTop是目标activity在栈顶才会复用,这个是只要栈内存在就会复用。跟singleTop一样的是,当栈内实例被复用时,可以复写onNewIntent方法接受intent并进行业务处理。


我们来谈一点具体的东西。当一个intent请求起一个activity A,这个A 配置lauchMode为singleTask,系统首先会寻找是否存在A想要的任务栈,如果不存在,就重新创建一个任务栈,然后创建A的实例并压入栈中,如果任务栈已经存在,那就看栈内是否已经有A的实例,如果没有创建新的实例压入栈中,如果有,就把A上面所有的activity全部销毁,使A位于栈顶,复用A的实例,并调用起onNewIntent方法。


举几个例子来看:

1. 目前任务栈S1中有ABC,这时候D以singleTask模式启动,其所需要的任务栈为S2,由于S2不存在,所以系统会先创建S2任务栈,再创建D的实例,并压入S2;

2. 另外一种情况,假设D需要的任务栈就是S1,由于S1已经有了,但是栈内没有D的实例,所以系统会创建D的实例并压入S1栈中,成了ABCD;

3. 如果D所需要的任务栈S1为ADBC,这时候要启动D,就会销毁B和C,栈内变为AD,再复用此时栈顶的D。


至此,你可能会对 “这个Activity所需要的task栈” 这种说法感到困惑。什么是一个activity所需要的任务栈,这要讲一个参数:TaskAffinity,任老师的翻译是:任务相关性。

这个参数和lauchMode一样,也是在manifest中声明activity时配置,如果没有配置这个参数,它会取默认值,默认值就是当前应用的包名,所以手动配置这个参数的话,就没必要指定为包名了。这个参数一般都是和singleTask启动模式或者allowTaskReparenting属性配对使用,在其他的情况下没有意义。


如果TaskAffinity和singleTask配对使用,他是具有singleTask启动模式的activity的目标任务栈的名字,也就是我们刚刚提到的它需要的任务栈。

如果TaskAffinity和allowTaskReparenting结合的时候,这种情况有点复杂,会产生特殊的效果。当一个应用A启动了应用B的某个activity之后,如果这个activity的allowTaskReparenting属性为true的话,那么当应用B被启动后,此Activity会直接从应用A的任务栈转移到应用B的任务栈中。这话有点抽象,我们来看一个例子:

现在有两个应用A和B,A启动了B里的一个activity C,然后按home键回到桌面,此时如果我们单击应用B的图标,默认情况下,应该启动B的主Activity,但是如果配置了这个属性的话,这个时候不是启动了B的主Activity,而是重新显示了被A启动的activity C。或者说,C从A的任务栈转移到了B的任务栈中。可以这样理解,由于A启动了activity C,所以C只能运行在A的任务栈中,但是C属于应用B,正常情况下,它的TaskAddinity肯定不会与A的任务栈名相同(因为包名不同),当B启动以后,B会创建自己的任务栈,这时候C发现自己想在的任务栈已经创建完成了,尤其就把C从A的任务栈中转移过来了。


singleInstance : 这是一种加强了的singleTask模式,他除了具有singleTask所有的特性外,还有一点就是,具有此种启动模式的activity只能单独的位于一个任务栈中。这种模式不常用,这里不做太多说明。


至此,我们介绍完了全部activity的启动模式,前面提到过如何给activity设置启动模式:在manifest里声明activity时,配置lanchMode这个属性。当然还有另外一种方法,是通过在Intent中设置标志位来为activity指定启动模式。

例如:

Intent intent = new Intent();

intent.setClass(MainActivity.this, SecondActivity.class);

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

startActivity(intent);


这两种方式都可以为activity指定启动模式,但是设置标志位的方法优先级高于第一种,当两种方法同时存在时,以标志位为准。

另外,两者各有限制:manifest中配置lauchMode方法不能单独设置起到FLAG_ACTIVITY_CLEAR_TOP标识,而标志位方法无法为activity指定singleInstance模式。

activity常用的FlAG:

FLAG_ACTIVITY_NEW_TASK : 新建一个task栈来管理这个activity;

FLAG_ACTIVITY_SINGLE_TOP:  和第一种方法指定singleTop的模式相同

FALG_ACTIVITY_CLEAR_TOP :  具有此标记的activity,当它启动时,在同一个任务栈位于它上面的activity都要销毁出栈,这个标志位和FLAG_ACTIVITY_SINGLE_TOP一起用,可以起到singleTask作用

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS: 具有这个标记的activity不会出现在历史的列表中,当某些情况下我们不希望用户通过历史列表回到我们的activity 时可以使用这个标志位。它等同于在manifest.xml中配置activity的属性:android:excludeFromRecents="true"。



最后,我们说的这么多都是理论,大家可以根据需要进行验证。可以提供一个不错的验证方法给大家。在手机连接电脑的情况下,在终端中输入:adb shell dumpsys activity 。

终端中会输出所有存在的任务栈和栈中的activity实例信息,方便我们验证。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值