activity栈主要用于管理activity的切换。当使用intent跳转至某个目标activity,需要根据目标activity的加载模式来加载。
activity一共有以下四种launchmode:
1.standard:默认,每次使用intent跳转到目标activity时都创建一个新的实例。坏处是每次进入都要创建新的实例,执行oncreate方法。
2.singletop:如果要跳转的目标activity正好在task的顶部(说明当前肯定不在目标task里,例如我在微信首页,然后想使用intent跳转到innoxyz应用的首页,那么加入innoxyz首页正好在innoxyz的task顶部,就直接跳转而不会创建实例),那么就直接跳转过去而不会创建新的。(例子,当前在home screen, 收到一条微信首页的推送,而此时task栈里的微信task里的activity栈里的最顶部正好就是微信首页,那么点击推送就直接进入该实例而不会创建新的实例)
3.singletask:会在一个新的task中产生这个实例,以后每次调用都会使用这个,不会去产生新的实例了。
4.singleinstance:是其所在栈的唯一activity,它会每次都被重用。
launchmode在多个activity跳转的过程中扮演着重要的角色,它可以决定是否生成新的activity实例,是否重用已存在的activity实例,是否和其他activity实例公用一个task里。这里简单介绍一下task的概念,task是一个具有栈结构的对象,一个task可以管理多个activity,启动一个应用,也就创建一个与之对应的task。
我们可以在androidmanifest.xml配置<activity>的android:launchmode属性为以上四种之一即可。
下面我们结合实例一一介绍这四种lanchmode:
1.standard
standard模式是默认的启动模式,不用为<activity>配置android:launchmode属性即可,当然也可以指定值为standard。
我们将会一个activity,命名为firstactivity,来演示一下标准的启动模式。firstactivity代码如下:
standard模式是默认的启动模式,不用为<activity>配置android:launchmode属性即可,当然也可以指定值为standard。
我们将会一个activity,命名为firstactivity,来演示一下标准的启动模式。firstactivity代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package com.scott.launchmode; import android.app.activity; import android.content.intent; import android.os.bundle; import android.view.view; import android.widget.button; import android.widget.textview; public class firstactivity extends activity { @override public void oncreate(bundle savedinstancestate) { super .oncreate(savedinstancestate); setcontentview(r.layout.first); textview textview = (textview) findviewbyid(r.id.textview); textview.settext( this .tostring()); button button = (button) findviewbyid(r.id.button); button.setonclicklistener( new view.onclicklistener() { @override public void onclick(view v) { intent intent = new intent(firstactivity. this , firstactivity. class ); startactivity(intent); } }); } } |
我们firstactivity界面中的textview用于显示当前activity实例的序列号,button用于跳转到下一个firstactivity界面。
然后我们连续点击几次按钮,将会出现下面的现象:
我们注意到都是firstactivity的实例,但序列号不同,并且我们需要连续按后退键两次,才能回到第一个fristactivity。standard模式的原理如下图所示:
如图所示,每次跳转系统都会在task中生成一个新的firstactivity实例,并且放于栈结构的顶部,当我们按下后退键时,才能看到原来的firstactivity实例。
这就是standard启动模式,不管有没有已存在的实例,都生成新的实例。
2.singletop
我们在上面的基础上为<activity>指定属性android:launchmode="singletop",系统就会按照singletop启动模式处理跳转行为。我们重复上面几个动作,将会出现下面的现象:
我们看到这个结果跟standard有所不同,三个序列号是相同的,也就是说使用的都是同一个firstactivity实例;如果按一下后退键,程序立即退出,说明当前栈结构中只有一个activity实例。singletop模式的原理如下图所示:
正如上图所示,跳转时系统会先在栈结构中寻找是否有一个firstactivity实例正位于栈顶,如果有则不再生成新的,而是直接使用。也许朋友们会有疑问,我只看到栈内只有一个activity,如果是多个activity怎么办,如果不是在栈顶会如何?我们接下来再通过一个示例来证实一下大家的疑问。
我们再新建一个activity命名为secondactivity,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package com.scott.launchmode; import android.app.activity; import android.content.intent; import android.os.bundle; import android.view.view; import android.widget.button; import android.widget.textview; public class secondactivity extends activity { @override protected void oncreate(bundle savedinstancestate) { super .oncreate(savedinstancestate); setcontentview(r.layout.second); textview textview = (textview) findviewbyid(r.id.textview); textview.settext( this .tostring()); button button = (button) findviewbyid(r.id.button); button.setonclicklistener( new view.onclicklistener() { @override public void onclick(view v) { intent intent = new intent(secondactivity. this , firstactivity. class ); startactivity(intent); } }); } } |
然后将之前的firstactivity跳转代码改为:
1
2
|
intent intent = new intent(firstactivity. this , secondactivity. class ); startactivity(intent); |
是的,firstactivity会跳转到secondactivity,secondactivity又会跳转到firstactivity。演示结果如下:
我们看到,两个firstactivity的序列号是不同的,证明从secondactivity跳转到firstactivity时生成了新的firstactivity实例。原理图如下:
我们看到,当从secondactivity跳转到firstactivity时,系统发现存在有firstactivity实例,但不是位于栈顶,于是重新生成一个实例。
这就是singletop启动模式,如果发现有对应的activity实例正位于栈顶,则重复利用,不再生成新的实例。
3.singletask
在上面的基础上我们修改firstactivity的属性android:launchmode="singletask"。演示的结果如下:
我们注意到,在上面的过程中,firstactivity的序列号是不变的,secondactivity的序列号却不是唯一的,说明从secondactivity跳转到firstactivity时,没有生成新的实例,但是从firstactivity跳转到secondactivity时生成了新的实例。singletask模式的原理图如下图所示:
在图中的下半部分是secondactivity跳转到firstactivity后的栈结构变化的结果,我们注意到,secondactivity消失了,没错,在这个跳转过程中系统发现有存在的firstactivity实例,于是不再生成新的实例,而是将firstactivity之上的activity实例统统出栈,将firstactivity变为栈顶对象,显示到幕前。也许朋友们有疑问,如果将secondactivity也设置为singletask模式,那么secondactivity实例是不是可以唯一呢?在我们这个示例中是不可能的,因为每次从secondactivity跳转到firstactivity时,secondactivity实例都被迫出栈,下次等firstactivity跳转到secondactivity时,找不到存在的secondactivity实例,于是必须生成新的实例。但是如果我们有thirdactivity,让secondactivity和thirdactivity互相跳转,那么secondactivity实例就可以保证唯一。
这就是singletask模式,如果发现有对应的activity实例,则使此activity实例之上的其他activity实例统统出栈,使此activity实例成为栈顶对象,显示到幕前。
4.singleinstance
这种启动模式比较特殊,因为它会启用一个新的栈结构,将acitvity放置于这个新的栈结构中,并保证不再有其他activity实例进入。
我们修改firstactivity的launchmode="standard",secondactivity的launchmode="singleinstance",由于涉及到了多个栈结构,我们需要在每个activity中显示当前栈结构的id,所以我们为每个activity添加如下代码:
1
2
|
textview taskidview = (textview) findviewbyid(r.id.taskidview); taskidview.settext( "current task id: " + this .gettaskid()); |
然后我们再演示一下这个流程:
我们发现这两个activity实例分别被放置在不同的栈结构中,关于singleinstance的原理图如下:
我们看到从firstactivity跳转到secondactivity时,重新启用了一个新的栈结构,来放置secondactivity实例,然后按下后退键,再次回到原始栈结构;图中下半部分显示的在secondactivity中再次跳转到firstactivity,这个时候系统会在原始栈结构中生成一个firstactivity实例,然后回退两次,注意,并没有退出,而是回到了secondactivity,为什么呢?是因为从secondactivity跳转到firstactivity的时候,我们的起点变成了secondactivity实例所在的栈结构,这样一来,我们需要“回归”到这个栈结构。
如果我们修改firstactivity的launchmode值为singletop、singletask、singleinstance中的任意一个,流程将会如图所示:
singleinstance启动模式可能是最复杂的一种模式,为了帮助大家理解,我举一个例子,假如我们有一个share应用,其中的shareactivity是入口activity,也是可供其他应用调用的activity,我们把这个activity的启动模式设置为singleinstance,然后在其他应用中调用。我们编辑shareactivity的配置:
1
2
3
4
5
6
7
8
9
10
|
<activity android:name= ".shareactivity" android:launchmode= "singleinstance" > <intent-filter> <action android:name= "android.intent.action.main" /> <category android:name= "android.intent.category.launcher" /> </intent-filter> <intent-filter> <action android:name= "android.intent.action.single_instance_share" /> <category android:name= "android.intent.category.default" /> </intent-filter> </activity> |
然后我们在其他应用中这样启动该activity:
1
2
|
intent intent = new intent( "android.intent.action.single_instance_share" ); startactivity(intent); |
当我们打开shareactivity后再按后退键回到原来界面时,shareactivity做为一个独立的个体存在,如果这时我们打开share应用,无需创建新的shareactivity实例即可看到结果,因为系统会自动查找,存在则直接利用。大家可以在shareactivity中打印一下taskid,看看效果。关于这个过程,原理图如下: