天天看点

Activity的四种launchMode

转自:http://blog.csdn.net/liuhe688/article/details/6754323/

我们今天要讲的是Activity的四种launchMode。

launchMode在多个Activity跳转的过程中扮演着重要的角色,它可以决定是否生成新的Activity实例,是否重用已存在的Activity实例,是否和其他Activity实例公用一个task里。这里简单介绍一下task的概念,task是一个具有栈结构的对象,一个task可以管理多个Activity,启动一个应用,也就创建一个与之对应的task。

Activity一共有以下四种launchMode:

  1. standard
  2. singleTop
  3. singleTask
  4. singleInstance

我们可以在AndroidManifest.xml配置

<activity>

Android:launchMode

属性为以上四种之一即可。

下面我们结合实例一一介绍这四种

lanchMode

1.standard

standard

模式是默认的启动模式,不用为配置

android:launchMode

属性即可,当然也可以指定值为

standard

每次启动一个

Activity

就会重新创建一个新的实例,不管这个实例是否已经存在。被创建的实例的生命周期符合典型情况下

Activity

的生命周期,它的

onCreate

,

onStart

,

onResume

都会被调用。这是一种典型的多实例实现,一个任务栈中可以有多个实例,每个实例也可以属于不同的任务栈。在这种模式下,谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity所在的任务栈中。比如

Activity A

启动了

Activity B

(B是

standard

模式),那么B就会进入A所在的栈中。不知大家是否注意到,当我们用

ApplicationContext

去启动

standard

模式的

Activity

的时候会报错,错误如下:

Activity的四种launchMode

相信这句话大家一定不会陌生,这是因为standard模式的Activity默认会进入启动它的Activity所属的任务栈中,但是由于非Activity类型的Context(如ApplicationContext)并没有所谓的任务栈,所以这就有问题了。解决这个问题的方法是为待启动的Activity指定

FLAG_ACTIVITY_NEW_TASK

标记位,这样启动的时候就会为它创建一个新的任务栈,这个时候启动

Activity

实际上是以

singleTask

模式启动的,大家可以仔细体会。

我们将会一个

Activity

,命名为

FirstActivity

,来演示一下标准的启动模式。

FirstActivity

代码如下:

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

界面。

然后我们连续点击几次按钮,将会出现下面的现象:

Activity的四种launchMode
Activity的四种launchMode
Activity的四种launchMode

我们注意到都是

FirstActivity

的实例,但序列号不同,并且我们需要连续按后退键两次,才能回到第一个

FristActivity

standard

模式的原理如下图所示:

Activity的四种launchMode

如图所示,每次跳转系统都会在

task

中生成一个新的

FirstActivity

实例,并且放于栈结构的顶部,当我们按下后退键时,才能看到原来的

FirstActivity

实例。

这就是

standard

启动模式,不管有没有已存在的实例,都生成新的实例。

2.singleTop

栈顶复用模式

在这种模式下,如果新Activity已经位于任务栈的栈顶,那么此

Activity

不会被重新创建,同时它的

onNewIntent

方法会被回调,通过此方法的参数我们可以取出当前请求的信息。需要注意的是,这个

Activity

onCreate

onStart

不会被系统调用,因为它并没有发生改变。如果新

Activity

的实例已存在但不是位于栈顶,那么新

Activity

仍然会重新创建。举个例子,假如目前栈内情况为ABCD,其中ABCD为四个

Activity

,A位于栈底,D位于栈顶,这个时候假设要再次启动D,如果D的启动模式为

singleTop

,那么栈内的情况仍然为ABCD,如果D的启动模式为

standard

,那么由于D被重新创建,导致栈内的情况就变为ABCDD。

我们在上面的基础上为指定属性

android:launchMode="singleTop"

,系统就会按照

singleTop

启动模式处理跳转行为。我们重复上面几个动作,将会出现下面的现象:

Activity的四种launchMode
Activity的四种launchMode
Activity的四种launchMode

我们看到这个结果跟

standard

有所不同,三个序列号是相同的,也就是说使用的都是同一个

FirstActivity

实例;如果按一下后退键,程序立即退出,说明当前栈结构中只有一个

Activity

实例。

singleTop

模式的原理如下图所示:

Activity的四种launchMode

正如上图所示,跳转时系统会先在栈结构中寻找是否有一个

FirstActivity

实例正位于栈顶,如果有则不再生成新的,而是直接使用。也许朋友们会有疑问,我只看到栈内只有一个

Activity

,如果是多个

Activity

怎么办,如果不是在栈顶会如何?我们接下来再通过一个示例来证实一下大家的疑问。

我们再新建一个

Activity

命名为

SecondActivity

,如下:

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

跳转代码改为:

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);  
startActivity(intent);  
           

是的,

FirstActivity

会跳转到

SecondActivity

SecondActivity

又会跳转到

FirstActivity

。演示结果如下:

Activity的四种launchMode
Activity的四种launchMode
Activity的四种launchMode

我们看到,两个

FirstActivity

的序列号是不同的,证明从

SecondActivity

跳转到

FirstActivity

时生成了新的

FirstActivity

实例。原理图如下:

Activity的四种launchMode

我们看到,当从

SecondActivity

跳转到

FirstActivity

时,系统发现存在有

FirstActivity

实例,但不是位于栈顶,于是重新生成一个实例。

这就是

singleTop

启动模式,如果发现有对应的

Activity

实例正位于栈顶,则重复利用,不再生成新的实例。

3.singleTask

栈内复用模式 clearTop

这是一种单实例模式,只要

Activity

在一个栈中存在,那么多次启动此

Activity

都不会重新创建实例,和

singleTop

一样,系统也会回调其

onNewIntent

。具体一点,当一个具有

singleTask

模式的

Activity

请求启动后,比如

Activity A

,系统首先会寻找是否存在

A

想要的任务栈(TaskAffinity参数可以指定要启动的Activity所在的任务栈),如果不存在,就重新创建一个任务栈,然后创建

A

的实例后把

A

放到栈中。如果存在

A

所需的任务栈,这时要看

A

是否在栈中有实例存在,如果有实例存在,那么系统就会把

A

调到栈顶并调用它的

onNewIntent

方法,如果实例不存在,就创建

A

的实例并把

A

压入栈中,举几个例子:

  • 目前任务栈

    S1

    中的情况为

    ABC

    ,这个时候

    Activity D

    singleTask

    模式请求启动,其所需要的任务栈为

    S2

    ,由于

    S2

    D

    的实例均不存在,所以系统会先创建任务栈

    S2

    ,然后再创建

    D

    的实例并将其入栈到

    S1

  • 另外一种情况,假设

    D

    所需的任务栈为

    S1

    ,其他情况如上面例子1所示,那么由于

    S1

    已经存在,所以系统会直接创建

    D

    的实例并将其入栈到

    S1

  • 如果

    D

    所需的任务栈为

    S1

    ,并且当前任务栈S1的情况为ADBC,根据栈内复用的原则,此时

    D

    不会重新创建,系统会把

    D

    切换到栈顶并调用其

    onNewIntent

    方法,同时由于

    singleTask

    默认具有

    clearTop

    的效果,会导致栈内所有在

    D

    上面的

    Activity

    全部出栈,于是最终

    S1

    中的情况为

    AD

    ,这一点比较特殊,在后面还会对此种情况详细地分析。

何谓某个Activity所需的任务栈?

这要从一个参数说起:TaskAffinity,可以翻译为任务相关性。这个参数标识了一个Activity所需要的任务栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名。当然我们可以为每个Activity都单独指定TaskAffinity属性,这个属性值必须不能和包名相同,否则就相当于没有指定。TaskAffinity属性主要和singleTask启动模式或者allowTaskReparenting属性配对使用,在其他情况下没有意义。另外,任务栈分为前台任务栈和后台任务栈,后台任务栈中的Activity处于暂停状态,用户可以通过切换将后台任务栈再次调到前台。

当TaskAffinity和singleTask启动模式配对使用的时候,它是具有该模式的Activity的目前任务栈的名字,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中。

在上面的基础上我们修改

FirstActivity

的属性

android:launchMode="singleTask"

。演示的结果如下:

Activity的四种launchMode

! 这里写图片描述

Activity的四种launchMode
Activity的四种launchMode

我们注意到,在上面的过程中,

FirstActivity

的序列号是不变的,

SecondActivity

的序列号却不是唯一的,说明从

SecondActivity

跳转到

FirstActivity

时,没有生成新的实例,但是从

FirstActivity

跳转到

SecondActivity

时生成了新的实例。

singleTask

模式的原理图如下图所示:

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

添加如下代码:

TextView taskIdView = (TextView) findViewById(R.id.taskIdView);  
taskIdView.setText("current task id: " + this.getTaskId());  
           

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

Activity的四种launchMode
Activity的四种launchMode

我们发现这两个

Activity

实例分别被放置在不同的栈结构中,关于

singleInstance

的原理图如下:

Activity的四种launchMode

我们看到从

FirstActivity

跳转到

SecondActivity

时,重新启用了一个新的栈结构,来放置

SecondActivity

实例,然后按下后退键,再次回到原始栈结构;图中下半部分显示的在

SecondActivity

中再次跳转到

FirstActivity

,这个时候系统会在原始栈结构中生成一个

FirstActivity

实例,然后回退两次,注意,并没有退出,而是回到了

SecondActivity

,为什么呢?是因为从

SecondActivity

跳转到

FirstActivity

的时候,我们的起点变成了

SecondActivity

实例所在的栈结构,这样一来,我们需要“回归”到这个栈结构。

如果我们修改

FirstActivity

launchMode

值为

singleTop

singleTask

singleInstance

中的任意一个,流程将会如图所示:

Activity的四种launchMode

singleInstance

启动模式可能是最复杂的一种模式,为了帮助大家理解,我举一个例子,假如我们有一个

share

应用,其中的

ShareActivity

是入口

Activity

,也是可供其他应用调用的

Activity

,我们把这个

Activity

的启动模式设置为

singleInstance

,然后在其他应用中调用。我们编辑

ShareActivity

的配置:

<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

Intent intent = new Intent("android.intent.action.SINGLE_INSTANCE_SHARE");  
startActivity(intent);  
           

当我们打开

ShareActivity

后再按后退键回到原来界面时,

ShareActivity

做为一个独立的个体存在,如果这时我们打开

share

应用,无需创建新的

ShareActivity

实例即可看到结果,因为系统会自动查找,存在则直接利用。大家可以在

ShareActivity

中打印一下

taskId

,看看效果。关于这个过程,原理图如下:

Activity的四种launchMode

继续阅读