详解Android开发中Activity的四种launchMode

时间:2021-11-10 22:06:52

activity栈主要用于管理activity的切换。当使用intent跳转至某个目标activity,需要根据目标activity的加载模式来加载。

activity一共有以下四种launchmode:

1.standard:默认,每次使用intent跳转到目标activity时都创建一个新的实例。坏处是每次进入都要创建新的实例,执行oncreate方法。

详解Android开发中Activity的四种launchMode

2.singletop:如果要跳转的目标activity正好在task的顶部(说明当前肯定不在目标task里,例如我在微信首页,然后想使用intent跳转到innoxyz应用的首页,那么加入innoxyz首页正好在innoxyz的task顶部,就直接跳转而不会创建实例),那么就直接跳转过去而不会创建新的。(例子,当前在home screen, 收到一条微信首页的推送,而此时task栈里的微信task里的activity栈里的最顶部正好就是微信首页,那么点击推送就直接进入该实例而不会创建新的实例)

详解Android开发中Activity的四种launchMode

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界面。
然后我们连续点击几次按钮,将会出现下面的现象:

详解Android开发中Activity的四种launchMode详解Android开发中Activity的四种launchMode详解Android开发中Activity的四种launchMode

我们注意到都是firstactivity的实例,但序列号不同,并且我们需要连续按后退键两次,才能回到第一个fristactivity。standard模式的原理如下图所示:

详解Android开发中Activity的四种launchMode

如图所示,每次跳转系统都会在task中生成一个新的firstactivity实例,并且放于栈结构的顶部,当我们按下后退键时,才能看到原来的firstactivity实例。
这就是standard启动模式,不管有没有已存在的实例,都生成新的实例。

2.singletop
我们在上面的基础上为<activity>指定属性android:launchmode="singletop",系统就会按照singletop启动模式处理跳转行为。我们重复上面几个动作,将会出现下面的现象:

详解Android开发中Activity的四种launchMode详解Android开发中Activity的四种launchMode详解Android开发中Activity的四种launchMode

我们看到这个结果跟standard有所不同,三个序列号是相同的,也就是说使用的都是同一个firstactivity实例;如果按一下后退键,程序立即退出,说明当前栈结构中只有一个activity实例。singletop模式的原理如下图所示:

详解Android开发中Activity的四种launchMode

正如上图所示,跳转时系统会先在栈结构中寻找是否有一个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。演示结果如下:

详解Android开发中Activity的四种launchMode详解Android开发中Activity的四种launchMode详解Android开发中Activity的四种launchMode

我们看到,两个firstactivity的序列号是不同的,证明从secondactivity跳转到firstactivity时生成了新的firstactivity实例。原理图如下:

详解Android开发中Activity的四种launchMode详解Android开发中Activity的四种launchMode详解Android开发中Activity的四种launchMode详解Android开发中Activity的四种launchMode

我们看到,当从secondactivity跳转到firstactivity时,系统发现存在有firstactivity实例,但不是位于栈顶,于是重新生成一个实例。
这就是singletop启动模式,如果发现有对应的activity实例正位于栈顶,则重复利用,不再生成新的实例。

3.singletask
在上面的基础上我们修改firstactivity的属性android:launchmode="singletask"。演示的结果如下:

 

我们注意到,在上面的过程中,firstactivity的序列号是不变的,secondactivity的序列号却不是唯一的,说明从secondactivity跳转到firstactivity时,没有生成新的实例,但是从firstactivity跳转到secondactivity时生成了新的实例。singletask模式的原理图如下图所示:

详解Android开发中Activity的四种launchMode

在图中的下半部分是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());

然后我们再演示一下这个流程:

详解Android开发中Activity的四种launchMode详解Android开发中Activity的四种launchMode

我们发现这两个activity实例分别被放置在不同的栈结构中,关于singleinstance的原理图如下:

详解Android开发中Activity的四种launchMode

我们看到从firstactivity跳转到secondactivity时,重新启用了一个新的栈结构,来放置secondactivity实例,然后按下后退键,再次回到原始栈结构;图中下半部分显示的在secondactivity中再次跳转到firstactivity,这个时候系统会在原始栈结构中生成一个firstactivity实例,然后回退两次,注意,并没有退出,而是回到了secondactivity,为什么呢?是因为从secondactivity跳转到firstactivity的时候,我们的起点变成了secondactivity实例所在的栈结构,这样一来,我们需要“回归”到这个栈结构。
如果我们修改firstactivity的launchmode值为singletop、singletask、singleinstance中的任意一个,流程将会如图所示:

详解Android开发中Activity的四种launchMode

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,看看效果。关于这个过程,原理图如下:

详解Android开发中Activity的四种launchMode