Activity启动模式之launchMode

来源:互联网 发布:云南水务待遇好吗 知乎 编辑:程序博客网 时间:2024/05/22 15:18

转载请注明出处:http://blog.csdn.net/u013220682/article/details/50592861

Activity作为Android的四大组件之一,相信大家它已经再熟悉不过了。在开发中我们有时候需要为Activity设置它的启动模式launchMode。Activity有四种启动模式,即launchMode属性值可以是四种: standard,singleTop,singleTask,singleInstance。那么这四种模式有什么区别呢?他们到底是什么样的逻辑呢?通过对官网的解读以及查询一些资料,自己对这四种模式有了整体的认识。废话不多说了,下面我们就揭开他们的神秘面纱。

在介绍这四种模式之前,先介绍一个概念,就是task任务栈,方便后面的理解。task是个具有栈结构的对象,一个task可以管理多个Activity启动一个应用,也就创建一个与之对应的task。

1. standard(标准模式)

这也是系统的默认模式。每次启动一个Activity都会创建一个新的实例,不管这个实例是否已经存在。下面我们写个Demo来证实我们所说的。

我们创建一个FirstActivity,其布局很简单,就一个Button。清单文件中的launchMode属性不用设置,默认为standard,当然你也可以自己再设置。FirstActivity中的代码如下:

public class FirstActivity extends ActionBarActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.d("Activity_launchMode", this.toString());Button mButton = (Button) findViewById(R.id.bt);mButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(FirstActivity.this, FirstActivity.class);startActivity(intent);}});}}
代码很简单,就是当Activity被创建后,首先日志输出该Activity的序列号。然后我们点击按钮时,则再启动一个FirstActivity。现在我打开应用点击按钮启动FirstActivity,然后我再点击按钮,又启动一次FirstActivity,我们看日志输出:


可以看出,每个FirstActivity实例的序列号都不一样,说明我们点击按钮时,FirstActivity都会重新创建实例。这就说明了standard模式每次启动一个Activity都会创建一个新的实例,不管这个实例是否已经存在。

注意:在这种模式下,谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity的栈中。比如A启动了B(B是标准模式),那么B就会进入到A所在的栈中。所以当我们用ApplicationContext去启动一个standard模式的Activity就会报错。因为非Activity类型的Context并没有所谓的任务栈,解决这个问题的方法是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动时就会为它创建一个新的任务栈。这时Activity实际上是以singleTask模式启动的,后面会提到该模式。

2. singleTop(栈顶复用模式)

在这种模式下,如果新的Activity实例已经存在并且位于任务栈的栈顶,那么此Activity不会重新创建,它的onNewIntent方法同时也会被回调。如果新的Activity实例存在但不位于任务栈的栈顶,那么该Activity仍然会重新创建。举个例子,假设目前栈内情况为ABCD,其中ABCD为四个Activity,A位于栈底,D位于栈顶。这个时候假设要再次启动D,如果D的启动模式为singleTop,那么启动后栈内情况仍然是ABCD;如果要再次启动C,C的启动模式也为singleTop,那么C会重新创建的,因为C不位于栈顶,此时栈内情况为ABCDC。

下面还是用代码来验证它:

还是用前面的Demo,这里我们需要把FirstActivity的launchMode属性设置为singleTop,如下:


然后运行程序,启动之后,点击按钮启动一次FirstActivity,再点击一次按钮又启动一次FirstActivity,下面我们看日志输出:


我们点击了两次按钮,结果只看到了一个输出日志,说明FirstActivity的onCreate方法没有调用,这就证实了当FirstActivity为于栈顶且启动模式为singleTop时,新的FirstActivity不会被创建。下面我们来证实当FirstActivity不位于栈顶的情况。

这里还是上面的Demo,这里我们新建一个Activity叫SecondActivity,布局和FirstActivity一样,里面有个按钮,模式就默认就行了。这里就不贴代码了。然后我们在FirstActivity中当点击按钮时,启动SecondActivity,代码如下:

public class FirstActivity extends ActionBarActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.d("Activity_launchMode", this.toString());Button mButton = (Button) findViewById(R.id.bt);mButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(FirstActivity.this, SecondActivity.class);startActivity(intent);}});}}
这时,FirstActivity不位于栈顶了,SecondActivity位于栈顶。在SecondActivity中,代码如下:

public class SecondActivity extends ActionBarActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.d("Activity_launchMode", this.toString());Button mButton = (Button) findViewById(R.id.bt);mButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(SecondActivity.this, FirstActivity.class);startActivity(intent);}});}}
在SecondActivity中,我们点击一次按钮,则再次启动FirstActivity。我们运行程序后看日志输出:


从日志我们可以看出,当在SecondActivity中启动FirstActivity时,FirstActivity的onCreate方法执行了,也就是FirstActivity被再次创建。这就说明了当FirstActivity的启动模式为singleTop时,如果不是位于栈顶,新的Activity还是会再次创建的。

注:这里onNewIntent方法的回调读者有兴趣可以自己去尝试。

3. singleTask(栈内复用模式)

这是一种单实例模式,只要Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,和singleTop一样,系统也会回调其onNewIntent方法。举几个例子:

(1)比如目前任务栈S1中的情况为ABC,这个时候D以singleTask模式请求启动,其所需的任务栈为S2,由于S2和D的实例均不存在,所以系统会先创建任务栈S2,然后在创建D的实例并将其入栈到S2。

(2)另一中情况,假设D所需的任务栈是S1,其他情况跟(1)中的相同。由于S1已经存在,D在S1中的实例不存在,所以系统会直接创建D的实例并将其入栈到S1。

(3)如果当前所需的任务栈为S1,并且S1中的情况为ADBC,根据栈内复用的原则,此时D不会重新创建,系统会把D调到栈顶且onNewI-ntent方法会被回调。同时由于singleTask有clearTop的效果,会导致栈内的所有在D上面的Activity全部出栈。这时候S1中的情况就为AD.这里我就不用Demo来验证了,有兴趣的可以自己写个Demo来证实一下,也很简单的。

注:上面说到的D所需要的任务栈,什么是Activity所需要的任务栈呢?不着急,我们先把第四种启动模式分析完再解释这个问题。

4. singleInstance(单实例模式)

这是一种加强的singleTask模式,它除了具有singleTask模式的所有特征外 ,还加强了一点。那就是具有此种模式的Activity只能单独的位于一个任务栈中。比如,Activity A是singleInstance模式,当A启动后,系统会为它创建一个新的任务栈,然后A独自在这个新的任务栈中,并且后续的请求均不会创建新的Activity,除非这个独特的任务栈被系统销毁了。

这就是Activity的四种启动模式,大家是不是对launchMode有了更清楚的认识了呢?前面在singleTask中提到的Activity所需要的任务栈问题,下面我来解释一下。

扩展:这要涉及到一个参数:TaskAffinity,可以翻译为任务相关性。这个参数标识了一个Activity所需要的任务栈的名字,默认所有Activity所需要的任务栈的名字就是应用的包名。当然我们可以为每个Activity都单独指定TaskAffinity属性,这个属性不能可包名相同,否则就相当于没有指定。TaskAffinity主要和singleTask或者allowTaskReparenting属性配对使用,在其他情况下没有意义。

当TaskAffinity与singleTask配对使用时,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中。

当TaskAffinity和allowTaskReparenting结合时,情况会复杂一些。比如,现在有两个应用A,B。A启动了B中的一个Activity C,如果这个Activity C的allowTaskReparenting属性为true的话,那么当按Home键回到桌面,然后在单击B的桌面启动图标,这个时候并不是启动了B的主Acvitity,而是重新显示了已经被应用A启动的Activity C,或者说C从A的任务栈转移到了应用B的任务栈中。这里读者可以自己写个例子去测试一下,这里我就不做演示了。

0 0