带你深入理解Activity启动模式(LaunchMode)

我们知道默认情况下,当我们多次启动同一个activity时,系统会创建多个实例并把他们一个个放入任务栈,当我们按back键,这些activity又会一个个退出。在讲activity的launchmode之前,我们有必要了解下“任务栈(Task Stack)”这个概念。在Android中是使用任务(Task)来管理Activity的,任务就是存放在栈里面的Activity的集合,这个栈就是称为任务栈。

一,Activity的LaunchMode

明白任务栈的概念后,我们再来了解Activity的启动模式。目前Activity有四种启动模式:standard、singleTop、singleTask、singleInstance。下面我们一一介绍:

  • standard:标准模式,这是系统默认的默认,也就是说你不设置Activity的launchMode时,默认的就是standard。在这种模式下,每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否存在。在这种模式下,谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity所在的栈,但是这是有条件的,前提是启动它的Activity不能是singleInstance,因为singleInstance只能独立于一个任务栈中,不能有其他的Activity实例,这个后面我们会验证。比如非singleInstance模式的Activity A启动了标准模式的Activity B,那么Activity B就会进入到Activity A所在的任务栈。
  • singleTop:栈顶复用模式。在这种模式下,如果Activity已经在任务栈的栈顶了,当再次启动同一个Activity的时候,这个Activity不会被重新创建,而且它的onNewIntent()方法会被调用,但是它的onCreate()、onStart()方法不会被调用。此模式下的Activity也会进入启动它的非singleInstance模式的Activity所在的任务栈中。
  • singleTask:栈内复用模式。在这种模式下,只要Activity存在栈内,那么多次启动这个Activity都不会重新创建实例,系统会调用它的onNewIntent()方法。此外有个需要注意的地方:singleTask有clear top的效果,也就是说会将其以上的Activity全部出栈。
  • singleInstance:这是singleTask的一种加强模式,除了singleTask所有特性以外,具有此模式的Activity只能单独位于一个任务栈中。

二、实例讲解

介绍完四种启动模式后,我们看下各个模式的具体的例子。先介绍下背景:我们定义四个Activity,launchMode分别为:standard、singleTop、singleTask、singleInstance。对应的Activity的名称分别为:ActivityA、ActivityB、ActivityC、ActivityD。然后我们还定义了其各自的taskAffinity,这个参数标示了该Activity所需要的任务栈的名字,默认情况下,所有的Activity所需的任务栈的名字为应用的包名。先看代码,AndroidManifest定义如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.jun.zhuzp.myapplication">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".launchmode.ActivityA"
            android:taskAffinity="com.jun.zhuzp.a1"
            android:launchMode="standard"/>
        <activity android:name=".launchmode.ActivityB"
            android:taskAffinity="com.jun.zhuzp.a2"
            android:launchMode="singleTop"/>
        <activity android:name=".launchmode.ActivityC"
            android:taskAffinity="com.jun.zhuzp.a3"
            android:launchMode="singleTask"/>
        <activity android:name=".launchmode.ActivityD"
            android:taskAffinity="com.jun.zhuzp.a4"
            android:launchMode="singleInstance"/>
    </application>

</manifest>

MainActivity是测试应用的主Activity,每个Activity中都有一个button,点击button就会去启动相应的Activity。我们在测试的过程中是这样的:MainActivity启动ActivityA,ActivityA启动ActivityB,ActivityB启动ActivityC,ActivityC启动ActivityD,然后ActivityD启动ActivityA,这样一个可以循环的过程。我们先看ActivityA的代码:

public class ActivityA  extends Activity{
    private static final String TAG = "ActivityA";
    private Button mSwitchButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate...");
        setContentView(R.layout.activity_launchmode);
        Log.d(TAG,"Task id = " + getTaskId());
        iniView();
    }

    private void iniView() {
        mSwitchButton = (Button)findViewById(R.id.btn_switch2);
        mSwitchButton.setText("启动ActivityB");
        mSwitchButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setClassName("com.jun.zhuzp.myapplication","com.jun.zhuzp.myapplication.launchmode.ActivityB");
                startActivity(intent);
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG,"onStart...");
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Log.d(TAG,"onNewIntent...");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume...");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy...");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop...");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause...");
    }
}

代码内容很简单,onCreate()方法中的getTaskId()用于打印该Activity所处的任务栈,然后就是对button设置了启动ActivityB的点击事件。其他几个Activity 的代码基本一样,只是button的点击事件不一样而已,这里就不贴代码了。接下来,我们来一步一步的分析我们写的测试效果。

  • standard模式的MainActivity启动ActivityA(说明:我在MainActivity的onCreate()方法中也加了getTaskId()的打印),我们先看下log
    这里写图片描述
    我们我们可以看到,两个Activity的Task id 是一样的,这就是说明,ActivityA进入了MainActivity所在的栈,然后我们再看下adb shell dumpsys activity 命令下的的结果:
    这里写图片描述
    我们可以看到Task id为89的任务栈的名称com.jun.zhuzp.myapplication。这是测试应用的包名,也是MainActivity任务栈名。但是我们在AndroidManifest文件中命名定义了ActivityA 的taskAffinity属性啊,它所在的任务栈仍然是MainActivity所在的任务栈,这是因为我们前面提到的:非singleInstance模式的Activity启动standard模式的Activity,被启动的Activity会进入启动它的那个Activity所在的任务栈中。那什么情况下,taskAffinity定义的任务栈才会起作用呢,想必大家也已经猜到了,就是singleInstance模式的Activity启动它时,才会创建一个名为taskAffinity所指定新的任务栈,这个结论会在singleInstance模式的ActivityD中启动standard ActivityA得到验证。

  • standard模式ActivityA启动singleTop模式的ActivityB。同样我们看log和adb shell dumpsys activity命令下的结果:
    这里写图片描述
    这里写图片描述
    和ActivityA类似,虽然为ActivityB设定了taskAffinity,但是ActivityB所在的任务栈仍然是MainActivity所在的任务栈,原因和前面standard模式一样。非singleInstance模式的Activity启动singleTop模糊的Activity,被启动的Activity会进入启动它的那个Activity所在的任务栈中,所以ActivityB和ActivityA在相同的任务栈中。

  • singleTop模式的ActivityB启动指定了taskAffinity的singleTask模式的ActivityC,照样先看logo和adb shell dumpsys activity结果:
    这里写图片描述
    这里写图片描述
    这个时候taskAffinity设定的任务栈名终于起作用了,这是为什么呢?因为当启动指定了任务栈的ActivityC时,系统会去判断ActivityC所需的任务栈名是否存在,如果不存在,就会创建其所需的任务栈,并将ActivityC压入创建的任务栈,这也就是上面所呈现的结果。那如果我们在AndroidManifest中没有设置ActivityC的android:taskAffinity属性时,那个ActivityC就是和ActivityA、ActivityB、MainActivity在同一个任务栈中。

  • singleTask模式的ActivityC启动singleInstance模式的ActivityD
    这里写图片描述
    这里写图片描述
    我们说过,singleInstance模式是singleTask的加强版,除了singleTask具有的特性外(这一点就在从logo和dumpsys activity结果可以看出),此种模式的Activity只能独立于一个任务栈中,这一点后面我们会验证。另外一点,如过没有指定singleInstance的任务栈名会怎样呢,系统还是会为它创建一个新的任务栈,任务栈名就是应用的包名,但是这并不表示没有指定任务栈名的ActivityD和MainActivity就在同一个任务栈中,虽然他们的任务栈名都是默认的应用包名,他们的Task id 是不一样的!!!

  • singleInstance模式ActivityD启动设置了taskAffinity属性的standard模式ActivityA
    这里写图片描述
    这里写图片描述
    这个时候,我们看到系统创建了一个新的任务栈——com.jun,zhuzp.a1。这就是我们为standard模式的ActivityA定义的taskAffinity指定的任务栈名,它终于起作用了!!!但是要铭记,它起作用的前提是singleInstance模式的Activity启动它,这是因为singleInstance模式任务栈中只能有一个Activity实例,standard模式的Activity想进也进不去啊,没办法系统只能再创建一个新的任务栈了,然后看到:额,还要个名字,那就给你命名吧。

  • 最后一点,我们来验证下singleTask模式的Activity具有clear top的功能,AndroidManifest文件,我们不变,我们改下启动Activity的方式:MainActivity—>ActivityC—>ActivityA—>ActivityB—>ActivityC。
    这是MainActivity—>ActivityC—>ActivityA—>ActivityB过程的任务栈情况,这个时候Task id为97的栈中有三个Activity,从栈顶到栈底依次是:ActivityB、ActivityA、ActivityC。singleTask模式的ActivityC在栈底。
    这里写图片描述
    然后我们再执行ActivityB—>ActivityC时,任务栈的情况:Task id 为97的栈中只有ActivityC里,另外两个都已经出栈了,这就是singleTask具有的clear top 的功能。同时,这个时候也不会走Activity的onCreate()方法了,会走onNewIntent()方法。
    这里写图片描述

补充:我们也可以在代码中通过给Intent来设置标志位来为Activity指定启动模式,例如:

Intent i = new Intent();
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.setClassName("com.jun.zhuzp.myapplication","com.jun.zhuzp.myapplication.launchmode.ActivityA");
startActivity(i);

但是这种方式还是和在AndroidManifest中设置launchMode有差别的,首先设置Activity的flag的优先级比在AndroidManifest中定义的优先级高;其次他们也有各自的使用范畴,比如AndroidManifest中只能定义四中:standard、singleTop、singleTask、singleInstance;flag就是很多种,具体大家可以看下文档。
这里我们列举部分Flag讲解
FLAGE_ACTIVITY_NEW_TASK,这个标志位对应于AndroidManifest中设置的“singleTask”
FLAGE_ACTIVITY_SINGLE_TOP,这个标志位对应于AndroidManifest中设置的“singleTop”
FLAGE_ACTIVITY_CLEAR_TOP,当Activity设置此标志位后,启动它时,在同一任务栈中所有位于它之上的Activity都会出栈。比如栈自底向上依次是:MainActivity—ActivityA—ActivityB,各个Activity 的launchMode还是给出的AndroidManifest中的定义,这个时候如果再启动ActivityA,同时设置了flag为FLAGE_ACTIVITY_CLEAR_TOP。那么栈内的自底向上就成了这样:MainActivity—ActivityA(此ActivityA实例不是之前栈内的那个,而是新的,之前的已出栈)。

三,总结

上面就是有关Activity启动模式的主要内容了,最后,我们通过一系列的图来总结下,方便大家理解。
1,standard,标准模式,每次启动都会创建新的实例,不管实例是否存在
这里写图片描述
2,singleTop,栈顶复用,如果Activity处于栈顶,则不会创建新的Activity
这里写图片描述
3,singleTask,栈内复用,只要Activity在栈内存在,那么多次启动该Activity都不会创建新的Activity实例。默认具有clear top的效果。
这里写图片描述
4,singleInstance,在singleTask的基础上,限制了此模式的Activity只能单独地位于一个任务栈中。
这里写图片描述

好了,以上就是关于launchmode的全部内容了,如果不正确的地方,还望指正。欢迎转载,转载请注明出处:带你理解深入Activity启动模式(LaunchMode)http://blog.csdn.net/zhuzp_blog/article/details/51367477

猜你喜欢

转载自blog.csdn.net/sd_zhuzhipeng/article/details/51367477