概述
在Android系统中,系统通过任务栈来保存并管理整个App的Activity,栈底的Activity是整个任务栈的发起者,一个任务栈中的Actvity可以来自不同的App,同一个App的Activity也可能不在一个任务栈中。在默认情况下,即所要启动的Activity启动模式为standard的情况下,每启动一个Activity,这个新启动的Activity都会创建一个实例,并进入启动它的那个Activity所在的任务栈,但是当连续启动多次同一个Activity,系统就会重复创建多个实例,并压入栈中,这显然是低效的,所以Android提供了另外三种launchMode:singleTop、singleTask和singleInstance,接下来详细介绍Android的四种启动模式。标准模式:standard
standard模式也是系统的默认模式。每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否已经存在,被创建的Activity的生命周期onCreate、onStart、onResume都会被一一调用。被启动的standard模式Activity将会进入启动它的Activity所在的任务栈。接下来用代码测试一下:package com.example.wsz.activitylaunchmodedemo;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "LaunchModeDemo";
private Button jumpBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "*****onCreate()方法******");
Log.i(TAG, "onCreate:" + getClass().getSimpleName() + " TaskId: " + getTaskId() );
dumpTaskAffinity();
setContentView(R.layout.activity_main);
jumpBtn = (Button) findViewById(R.id.first_btn);
jumpBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(MainActivity.this, SecondActivity.class);
startActivity(intent);
}
});
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.i(TAG, "*****onNewIntent()方法*****");
Log.i(TAG, "onNewIntent:" + getClass().getSimpleName() + " TaskId: " + getTaskId() );
dumpTaskAffinity();
}
protected void dumpTaskAffinity(){
try {
ActivityInfo info = this.getPackageManager()
.getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
Log.i(TAG, "taskAffinity:"+info.taskAffinity);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
一共创建三个Activity,启动模式都是standard,三个Activity代码大同小异,除了启动的Activity不同外,几乎都一样,就不贴代码了,其中MainActivity启动SecondActivity,SecondActivity可以启动自身和ThirdActivity,ThirdActivity可以启动自身和SecondActvity。下图是测试结果
启动的四个Activity都在同一个任务栈里
再用 adb shell dumpsys actvity看下任务栈的情况
我们可以看到,任务栈里确实有四个Activity,包括我们连续启动两次的ThirdActivity。
栈顶复用模式:singleTop
singleTop模式,以此模式启动的新Activity,如果已经处在任务栈的栈顶了,那么新Activity不会被重新创建,即新Activity的onCreate、onStart、onResume生命周期不会被调用,而是回调它的onNewIntent方法。注意,只有当要启动的新Activity已经处于栈顶才会复用,如果要启动的新Activity已经存在与任务栈中,但是并不是处于栈顶位置,那么系统还是会如standard模式一样去创建新Activity的实例。接下来我们将SecondActivity的启动模式改为singleTop
下面是测试结果
我们由MainActivity启动SecondActivity,在SecondActivity启动一次自身,然后再启动ThirdActivity,最后在ThirdActivity启动SecondActivity。实验结果和我们预测的一样,只有当SecondActivity处于栈顶时,才会复用,并回调onNewIntent方法。
再看看任务栈情况
我们在SecondActivity启动的SecondActivity自身并没有出现在任务栈中,而在ThirdActivity启动的SecondActivity,虽然任务栈中已经存在一个SecondActivity,但由于SecondActivity并不在栈顶,于是又重新创建了一个实例。
栈内复用模式:singleTask
与singleTask模式密切相关的有一个很重要的参数--taskAffinity。在standard模式和singleTop模式,我们说新Activity由哪个启动,就进入那个Activity所在的任务栈,但是在singleTask模式中,以此模式启动的Activity,系统会先去找这个Activity想要的那个任务栈,如果不存在这个任务栈,就创建这个任务栈,在往下执行相关操作,如果存在这个任务栈,直接往下执行相关操作。那么系统是怎么判断任务栈是不是Activity想要的呢?这就是taskAffinity的作用,它标明了一个Activity所需要的任务栈的名字,这个参数可以自行设置,默认情况下是与应用的包名一致。
注意:只在singleTask模式或者配合allowTaskReparenting属性配合时才有用,其他情况下没有意义。与allowTaskReparenting属性配合的效果大概是,应用A启动应用B的的一个Activity C,从应用A回到桌面,打开应用B,此时应用B的Activity并不是原本应该出现的第一个Activity,而是Activity C.
接下来正式说说关于singleTask模式的特性。
singleTask模式与singleTop模式的一大区别是,singleTop只是栈顶复用,只有当新Activity处于栈顶时才会复用,而singleTask只要存在与任务栈中就可以复用,复用时,同样回调onNewIntent方法。同时,在复用时,系统会将任务栈中处于将要启动的singleTask模式的Activity上面的Activity全部清空,也就是clearTop的效果。
具体描述一下启动过程:Activity A要启动singleTask模式的Activity B,此时系统会先寻找Activity B想要的任务栈,也就是与Activity B的taskAffinity的值是否一样,如果不存在这么一个任务栈,就新建一个任务栈,然后创建ActivityB的实例;如果存在,就判断这个任务栈内存不存在Activity
B,如果不存在,创建实例;如果存在,就采取复用,将处于Activity B上面的Activity全部清空,使其处于栈顶,并回调onNewIntent方法,启动完毕。
接下来我们用代码实验一下:
先不修改SecondActivity的taskAffinity值,将SecondActivity的启动模式改为singleTask
启动顺序为MainActivity>SecondActivity>ThirdActivity>SecondActivity
看下实验结果
我们可以看到,在ThirdActivity启动SecondActivity时,确实有复用,回调了SecondActivity的onNewIntent方法,并且将任务栈中的SecondActivity上面ThirdActivity清空了。
接下来修改下SecondActivity的taskAffinity,设为com.example.wsz.anothertask
启动顺序依然是MainActivity>SecondActivity>ThirdActivity>SecondActivity
看下实验结果
可以看到有两个任务栈,ThirdActivity进入SecondActivity的任务栈,并在再次启动SecondActivity时被清空。
单实例模式:singleInstance
singleInstance是一种加强版的singleTask,它除了singleTask所具有的全部特性外,还有一点,就是以这种模式启动的Activity只能单独存在于一个任务栈中,这个任务栈只能存在这一个Activity,即便是由这个Activity再启动另外一个Activity,这个新Activity也不能进入这个任务栈。
接下来实验一下,将SecondActivity的启动模式改为singleInstance
启动顺序依然是MainActivity>SecondActivity>ThirdActivity>SecondActivity
看下结果
我们可以看到从MainActivity启动SecondActivity时新创建了一个任务栈;而由SecondActivity启动ThirdActivity时,ThirdActivity并没有进入SecondActivity的任务栈,而是进入了MainActivity的任务栈;接下来从ThirdActivity启动SecondActivity时,进行服用,回调了onNewIntent方法。
注:对于singleTask和singleInstance模式的Activity用startActivityForResult方法启动返回的resultCode会是Activity.RESULT_CANCELED,因为不同任务栈默认无法通信。
Intent Flag与启动模式相关的部分
Intent的flag有很多,其中有几个和启动模式相关的
Intent.FLAG_ACTIVITY_SINGLE_TOP:使用singleTop模式启动Activity,与制定Android:launchMode=“singleTop”一样
Intent.FLAG_ACTIVITY_NEW_TASK:使用singleTask模式启动Activity,与制定Android:launchMode=“singleTask”一样,常用于从Service启动Activity,因为Service没有任务栈
Intent.FLAG_ACTIVITY_CLEAR_TOP:使用此标志启动Activity时,同一任务栈中处于它上面的Activity将全部被出栈,当被启动的Activity是standard模式时,会将任务栈中的此Activity及此Activity上面的所有Activity出栈,然后创建新的实例并如栈。
Intent.FLAG_ACTIVITY_NO_HISTORY:以此标志启动的Activity,在启动其他Activity后,不会保留在任务栈中
当以Flag与AndroidManifest设置的启动模式冲突时,以AndroidManifest设置为准,例如AndroidManifest设置的启动模式为standard,而Intent的Flag设置为FLAG_ACTIVITY_SINGLE_TOP时,启动模式为standard。