美文网首页AndroidAndroid开发技术Android Framework源码分析
Android-Activity所应该了解的大概就这样。(中)

Android-Activity所应该了解的大概就这样。(中)

作者: 阿敏其人 | 来源:发表于2015-12-04 18:28 被阅读6605次

    本文出自 “阿敏其人” 简书博客,转载或引用请注明出处。

    Android-Activity所应该了解的大概就这样。(上)
    Android-Activity所应该了解的大概就这样。(下)

    五、任务栈/Activity的启动模式

    知道生命周期、线程的优先级和Activity的异常销毁,下面我们来认识一下任务栈。

    我们上面进行的那么多描述和代码,都是在standard这种默认的任务栈进行的。

    栈的概念这里就不再赘述,这里知道先进后出就好。就像子弹夹。

    不同的任务栈,也称为不同的启动模式。

    1、任务栈的分类:

    任务栈有以下四种

    • ** standard 默认的启动模式,标准模式**
    • ** singletop 单一顶部模式 (顶部不会重复)**
    • ** singleTask 单一任务栈,干掉头上的其他Activity**
    • ** singleInstance 单一实例(单例),任务栈里面自已自己一个人**

    一般来说用默认的就好,当我们程序感觉切换奇怪,或者某个activity占用开销太大之类的,才考虑使用其他的启动默认。

    2、指定任务栈Activity的启动模式

    待会我们再来详细解释这些不同的任务栈详细区别,现在,我们先看一下怎么指定一个Activity的任务栈模式,也就是启动模式。

    默认的Activity都是standard模式的,那如果我们要把一个Activity指定为 singleTask 模式呢?

    有两种启动方法:一种方法是manifest指定,另外一种方式是代码指定。

    2.1 manifest指定

    比如我们要指定为singleTask模式
    manifest里面的Activity有个 launchMode属性来制定启动模式:

            <activity android:name=".SecondActivity"
                
                android:launchMode="singleTask"
                
                />
    

    2.2 代码指定 intent.addFlag

    比如我们要指定为singleTop模式

    Intent intent  = new Intent();
    intent.setClass(FirstActivity.this,SecondActivity.class);
    // 通过Intent的addFlag指定
    intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    startActivity(intent);
    

    2.3、两种方式的对比

    1、从优先级来说,代码指定优先于manifest指定
    2、两者各有局限,
    manifest无法设定 FLAG_ACTIVITY_CLEAR_TOP 标识
    代码指定无法指定为 singleInstance启动模式

    3、怎么查看当前app的任务栈数量和任务栈里面的Activity

    3.1 adb shell dumpsys activity

    在终端键入这样的指令:
    adb shell dumpsys activity

    查看当前手机的任务栈运行情况

    即可得到我们想要的信息,回车后会列出相当多的信息,我们需要找到如下的分类
    ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)

    下面是一份示例log摘取:

    ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
      Stack #0:
        Task id #1
          TaskRecord{529322d8 #1 A=com.android.launcher U=0 sz=1}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10600000 cmp=com.android.launcher/com.android.launcher2.Launcher }
            Hist #0: ActivityRecord{5297586c u0 com.android.launcher/com.android.launcher2.Launcher t1}
              Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000000 cmp=com.android.launcher/com.android.launcher2.Launcher }
              ProcessRecord{5293299c 17717:com.android.launcher/u0a8}
    
        Running activities (most recent first):
          TaskRecord{529322d8 #1 A=com.android.launcher U=0 sz=1}
            Run #0: ActivityRecord{5297586c u0 com.android.launcher/com.android.launcher2.Launcher t1}
    
      Stack #1:
        Task id #25
          TaskRecord{52a1682c #25 A=com.amqr.taskstack U=0 sz=3}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.amqr.taskstack/.FirstActivity }
            Hist #2: ActivityRecord{52980338 u0 com.amqr.taskstack/.ThirdActivity t25}
              Intent { cmp=com.amqr.taskstack/.ThirdActivity }
              ProcessRecord{5296870c 32225:com.amqr.taskstack/u0a85}
            Hist #1: ActivityRecord{5297ef0c u0 com.amqr.taskstack/.SecondActivity t25}
              Intent { flg=0x20000000 cmp=com.amqr.taskstack/.SecondActivity }
              ProcessRecord{5296870c 32225:com.amqr.taskstack/u0a85}
            Hist #0: ActivityRecord{529f1cc8 u0 com.amqr.taskstack/.FirstActivity t25}
              Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.amqr.taskstack/.FirstActivity }
              ProcessRecord{5296870c 32225:com.amqr.taskstack/u0a85}
    
        Running activities (most recent first):
          TaskRecord{52a1682c #25 A=com.amqr.taskstack U=0 sz=3}
            Run #2: ActivityRecord{52980338 u0 com.amqr.taskstack/.ThirdActivity t25}
            Run #1: ActivityRecord{5297ef0c u0 com.amqr.taskstack/.SecondActivity t25}
            Run #0: ActivityRecord{529f1cc8 u0 com.amqr.taskstack/.FirstActivity t25}
    
        mResumedActivity: ActivityRecord{52980338 u0 com.amqr.taskstack/.ThirdActivity t25}
    
      mFocusedActivity: ActivityRecord{52980338 u0 com.amqr.taskstack/.ThirdActivity t25}
      mDismissKeyguardOnNextActivity=false
      mFocusedStack=ActivityStack{52a267fc stackId=10, 1 tasks} mStackState=STACK_STATE_HOME_IN_BACK
      mSleepTimeout=false
      mCurTaskId=25
      mUserStackInFront={}
    
      Recent tasks:
      * Recent #0: TaskRecord{52a1682c #25 A=com.amqr.taskstack U=0 sz=3}
      * Recent #1: TaskRecord{529322d8 #1 A=com.android.launcher U=0 sz=1}
      * Recent #2: TaskRecord{529890a0 #11 A=com.android.systemui U=0 sz=0}
    
    
    

    3.2、 ACTIVITY MANAGER ACTIVITIES

    在ACTIVITY MANAGER ACTIVITIES 大分类里,
    找到Running activities (most recent first),可以看到最近正在操作的那个程序涉及到任务栈个数,以及每个任务栈的Activity数量,根据位置上下排序,最上面的是栈顶,最下面的是栈底。栈顶就是最近的操作界面

    3.3、Running activities (most recent first)

    我们把Running activities (most recent first)单独拿出来说。
    下面这份信息,显示着最近运行的一个程序的只有一个任务栈,任务栈里面有三个activit,栈顶是ThirdActivity,栈底是FirstActivity。
    这个任务栈的名称是com.amqr.taskstack。

        Running activities (most recent first):
          TaskRecord{52a1682c #25 A=com.amqr.taskstack U=0 sz=3}
            Run #2: ActivityRecord{52980338 u0 com.amqr.taskstack/.ThirdActivity t25}
            Run #1: ActivityRecord{5297ef0c u0 com.amqr.taskstack/.SecondActivity t25}
            Run #0: ActivityRecord{529f1cc8 u0 com.amqr.taskstack/.FirstActivity t25}
    

    再来一份例子:

        Running activities (most recent first):
          TaskRecord{528f99b4 #12 A=com.taskstack.thirdtask U=0 sz=1}
            Run #1: ActivityRecord{529d828c u0 com.amqr.taskstack/.ThirdActivity t12}
          TaskRecord{529e4584 #11 A=com.amqr.taskstack U=0 sz=1}
            Run #0: ActivityRecord{529a8c5c u0 com.amqr.taskstack/.FirstActivity t11}
    
    

    比如这份终端信息。就表示当前当前最近的运行的有两个任务栈的,(为什么一个程序的运行有两个任务栈?这就涉及到singleTask或者singleInstance启动模式了,后面会细说。)
    其中,一个任务栈的是com.taskstack.thirdtask,(我们自己指定的名称),当前这个任务栈里面只有一个activity。
    另外一个任务栈的名称是com.amqr.taskstack,这个是系统默认自带的任务栈,名字就是包名。

    我们拿出一条来分析把

    TaskRecord{528f99b4 #12 A=com.taskstack.thirdtask U=0 sz=1} 
      Run #1: ActivityRecord{529d828c u0 com.amqr.taskstack/.ThirdActivity t12}
    

    像这个,528f99b4 #12这么一串里面,这个#12可以说是这个任务栈的唯一标示(有时候会两个任务栈出现 任务栈A=“aaa.bbb.ccc”的aaa.bbb.ccc一样的情况,想要根本区分�是否为同一个任务栈,用的就是这个 #号+num,如果两个人任务栈的 #+num 和 A="aaa.bbb.ccc" 两者都一致的话,那么这两个任务栈绝对会同一个任务栈)
    至于最后面的 sz=num,这个num代表这个任务栈里面当前存放了多少个Activity。

    什么时候出现两个任务栈 aaa..bbb.ccc 相同的但是 #num 不同:singleInstance
    什么时候出现两个任务栈 aaa.bbb.ccc 和 #num 都相同:singleTask + taskAffinity

    3.4、 Recent tasks 手机当前的运行的任务栈

    (注意:Recent tasks代表的最近手机运行的程序的任务栈,不是对应正在运行的程序的个数(因为有的程序可能有多个任务栈),更加不是进程数。)

      Recent tasks:
      * Recent #0: TaskRecord{52a1682c #25 A=com.amqr.taskstack U=0 sz=3}
      * Recent #1: TaskRecord{529322d8 #1 A=com.android.launcher U=0 sz=1}
      * Recent #2: TaskRecord{529890a0 #11 A=com.android.systemui U=0 sz=0}
    

    这个是记录手机当前的运行的任务栈数量的。
    当我们长按home键,或者按下虚拟的菜单键,(因机型而异),反正就是列出正在运行的运行的任务栈的界面,假设我们的正在运行的是1个任务栈,一般来说,这时Rcent tasks就会显示记录着3个任务栈,如果我们手机显示正在运行两个2个,那么终端的Rcent tasks就会显示记录着4个任务栈,即为N+2个。(有时候会是N+1,只有com.android.launcher )
    多出来的那个两个上面写的很清楚,那么就是com.android.launcher 和 com.android.systemui,即为系统启动器和系统UI界面。

    我们在来个图文详细点的吧。

    当前手机任务列表运行着3个任务栈(不要以为任务栈就是程序,一个程序可能有多个任务栈)


    当前手机任务列表运行着3个任务栈.png

    查看一下终端,会显示记录着5个。

      Recent tasks:
      * Recent #0: TaskRecord{52a0c9e4 #16 A=com.android.systemui U=0 sz=1}
      * Recent #1: TaskRecord{528ea064 #1 A=com.android.launcher U=0 sz=1}
      * Recent #2: TaskRecord{529e0c18 #18 A=android.task.mms U=0 sz=1}
      * Recent #3: TaskRecord{529b3f00 #17 A=android.task.contacts U=0 sz=1}
      * Recent #4: TaskRecord{529589e4 #15 A=android.task.browser U=0 sz=1}
    
    

    .
    .
    再次说明,列表显示的是任务栈,不是程序


    一个程序的两个任务栈.png

    .
    .

    4、四种启动模式详解

    4.1、 standard 默认的启动模式,标准模式

    结论:每开启一个Activity,就会在栈顶添加一个Activity实例。多次间隔或者直接启动一个甲Activity会添加多个甲的示例,可重复添加。(间隔 ABA, 直接 ACC或者AAA)
      这里我们需要明白一个事情,Service和ApplicationContext是没办法直接开启一个新的Activity,因为只有Activity类型的Context的Activity才能开启,但还是有解决办法的,那就是让我们要开的那个新的Activity设置为FLAG_ACTIVITY_NEW_TASK标识。

    情景实测

    比如我们的程序里面有FirstActivity,SecondActivity、ThirdActivity和Fourth四个Activity,为了表示方便,我们就用A,B,C和D来分别指代吧。其中,A为启动页。(当前A,B,C,D都是standard模式)

    • 情况1: 启动后显示A,接着打开B,紧接着打开C。那么显而易见,这时候只有一个任务栈,假设为S1
      启动A:任务栈S1里面只有A
      接着打开B:任务栈里面变成BA,B在A上面,B为栈顶。
      接着打开C,任务栈里面变成了CBA,栈顶是C,栈底是A。

    大概是这个样子

    standard模式简单ABC手机界面.gif standard模式简单ABC.gif

    后面不会这么贴图了,第一个就图文说的清楚一些。

    • 情况2: 打开A,打开B,再次打开A
      此时任务栈: ABA
      那么任务栈里面的是 ABA , 其中第一次打开的A位于栈底,第二次打开的A为了栈顶。

    利用adb shell dumpsys activity查看Running activities (most recent first)可以得到证实:

        Running activities (most recent first):
          TaskRecord{52995938 #35 A=com.amqr.taskstack U=0 sz=3}
            Run #2: ActivityRecord{52a02754 u0 com.amqr.taskstack/.FirstActivity t35}
            Run #1: ActivityRecord{529fdd68 u0 com.amqr.taskstack/.SecondActivity t35}
            Run #0: ActivityRecord{529ed754 u0 com.amqr.taskstack/.FirstActivity t35}
    
    
    • 情况3:打开A,打开C,再打开C
      任务栈 CCA,其中第二个C是栈顶,A位于栈底。
    Running activities (most recent first):
          TaskRecord{529e1bac #4 A=com.amqr.taskstack U=0 sz=3}
            Run #2: ActivityRecord{529ae2d8 u0 com.amqr.taskstack/.ThirdActivity t4}
            Run #1: ActivityRecord{5299b11c u0 com.amqr.taskstack/.ThirdActivity t4}
            Run #0: ActivityRecord{529379d0 u0 com.amqr.taskstack/.FirstActivity t4}
    
    
    

    .
    顺便附上两次打开C的生命周期方法log,附上这个log是为了跟singleTask的做对比。

    12-03 07:15:21.176 15445-15445/com.amqr.taskstack D/Cur: FirstActicity onCreate
    12-03 07:15:21.176 15445-15445/com.amqr.taskstack D/Cur: FirstActivity onStart
    12-03 07:15:21.176 15445-15445/com.amqr.taskstack D/Cur: FirstActivity onResume
    第一次按下打开C
    12-03 07:15:23.360 15445-15445/com.amqr.taskstack D/Cur: FirstActivity onPause
    12-03 07:15:23.376 15445-15445/com.amqr.taskstack D/Cur: ThirdActivity onCreate
    12-03 07:15:23.376 15445-15445/com.amqr.taskstack D/Cur: ThirdActivity onStart
    12-03 07:15:23.376 15445-15445/com.amqr.taskstack D/Cur: ThirdActivity onResume
    12-03 07:15:23.788 15445-15445/com.amqr.taskstack D/Cur: FirstActivity onStop
    第二次按下打开C
    12-03 07:15:25.668 15445-15445/com.amqr.taskstack D/Cur: ThirdActivity onPause
    12-03 07:15:25.688 15445-15445/com.amqr.taskstack D/Cur: ThirdActivity onCreate
    12-03 07:15:25.688 15445-15445/com.amqr.taskstack D/Cur: ThirdActivity onStart
    12-03 07:15:25.688 15445-15445/com.amqr.taskstack D/Cur: ThirdActivity onResume
    12-03 07:15:26.068 15445-15445/com.amqr.taskstack D/Cur: ThirdActivity onStop
    
    

    附上FirstActivity的代码,其他三个Activity代码类似。

    public class FirstActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_first);
            Log.d("Cur", "FirstActicity onCreate");
    
            findViewById(R.id.mBtn1).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    startActivity(new Intent(FirstActivity.this, FirstActivity.class));
                }
            });
    
            findViewById(R.id.mBtn2).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
    
    
                    startActivity(new Intent(FirstActivity.this,SecondActivity.class));
    
                    /*Intent intent  = new Intent();
                    intent.setClass(FirstActivity.this,SecondActivity.class);
                    // 通过Intent的addFlag指定 ,这里指定为 single task
                    intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    
                    startActivity(intent);*/
                }
            });
    
            findViewById(R.id.mBtn3).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    startActivity(new Intent(FirstActivity.this,ThirdActivity.class));
                }
            });
    
            findViewById(R.id.mBtn4).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    startActivity(new Intent(FirstActivity.this,FourtActivity.class));
                }
            });
    
        }
    }
    
    

    manifest

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.amqr.taskstack" >
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme" >
            <activity android:name=".FirstActivity" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
            <activity android:name=".SecondActivity" />
            <activity android:name=".ThirdActivity"/>
            <activity android:name=".FourtActivity"/>
        </application>
    
    </manifest>
    
    

    .
    .

    4.2、singletop 单一顶部模式 (顶部不会重复)

    结论:如果开启的Activity已经存在一个实例在任务栈的顶部(仅限于顶部),再去开启这个Activity,任务栈不会创建新的Activity的实例了,而是复用已经存在的这个Activity,onNewIntent方法被调用;之前打开过,但不是位于栈顶,那么还是会产生新的实例入栈,不会回调onNewIntent方法。

    当我们把一个Activity设置为singleTop,当我们点击打开这个Activity的时候,我们打开B页面,会出现几种情况:

    说明:当前A和C都是Standard,B是singleTop

    之前没打开过:
    此时任务栈里面只有A,A所在的任务栈是S1,这个时候打开singleTop的B,B入栈,入的是S1这个栈,谁打开它进入谁的栈,此时S1的情况是BA,B为栈顶。

    之前打开过,但是位于栈顶:
    那么复用这个栈,不会有新的实例压入栈中。同时 onNewIntent 方法会被回调,我们可以利用这个方法获得页面传过来的消息或者其他操作。

    之前打开过,但是不是位于栈顶:
    那么还是会产生新的实例入栈。

    情景实测

    实测前,我们不改动Standard的代码,只是在manifest里面的将SecondActivity的启动模式设置为singleTop

    <activity android:name=".SecondActivity"    
    android:launchMode="singleTop"    />
    

    以下的谈论都是在A和C都是Standard模式,B为singleTop模式的情况进行的。

    • 情景一:打开A,然后在打开B
      没什么好说的,很普通的没什么区别

    请出终端大哥

        Running activities (most recent first):
          TaskRecord{529c0838 #19 A=com.amqr.taskstack U=0 sz=2}
            Run #1: ActivityRecord{52a35f58 u0 com.amqr.taskstack/.SecondActivity t19}
            Run #0: ActivityRecord{52a6c77c u0 com.amqr.taskstack/.FirstActivity t19}
    
    

    生命周期log

    12-03 07:57:20.728 6639-6639/? D/Cur: FirstActicity onCreate
    12-03 07:57:20.728 6639-6639/? D/Cur: FirstActivity onStart
    12-03 07:57:20.728 6639-6639/? D/Cur: FirstActivity onResume
    
    12-03 07:57:21.988 6639-6639/com.amqr.taskstack D/Cur: FirstActivity onPause
    12-03 07:57:22.004 6639-6639/com.amqr.taskstack D/Cur: SecondActivity onCreate
    12-03 07:57:22.004 6639-6639/com.amqr.taskstack D/Cur: SecondActivity onStart
    12-03 07:57:22.004 6639-6639/com.amqr.taskstack D/Cur: SecondActivity onResume
    12-03 07:57:22.428 6639-6639/com.amqr.taskstack D/Cur: FirstActivity onStop
    
    
    • 情况2:打开A,打开B,然后再打开B
      这个时候singleTop的作用就发挥出来了。这是不会产生新的实例,会复用的顶部的已经存在的实例。
      第一次打开B,这时任务栈里面的顺序是BA,B为栈顶
      第二次打开B,这时任务栈里面的顺序还是BA,B为栈顶。

    请出终端大哥:

        Running activities (most recent first):
          TaskRecord{52b29134 #21 A=com.amqr.taskstack U=0 sz=2}
            Run #1: ActivityRecord{5295bdf8 u0 com.amqr.taskstack/.SecondActivity t21}
            Run #0: ActivityRecord{52a395a8 u0 com.amqr.taskstack/.FirstActivity t21}
    
    
    

    生命周期log
    简单说可以认为,由于在我们不开新的实例的基础上打开自己,所以就只经历了一个 焦点的失去和获取 的过程。
    在SecondActivity中复写onNewIntent方法

        @Override
        protected void onDestroy() {
            super.onDestroy();
            Log.d("Cur", "SecondActivity onDestroy");
        }
    
        // onNewIntent 方法,当被复用时调用
        @Override
        protected void onNewIntent(Intent intent) {
            super.onNewIntent(intent);
            Log.d("Cur", "SecondActivity onNewIntent");
        }
    

    SecondActivity 的onNewIntent方法被调用了,代表被复用。
    (需要注意的是,当我们第二次打开B的时候,因为没有新的实例,所以不存在onCreate和onStart方法的执行,也不存在onStop)

    12-03 08:02:05.564 9277-9277/? D/Cur: FirstActicity onCreate
    12-03 08:02:05.564 9277-9277/? D/Cur: FirstActivity onStart
    12-03 08:02:05.564 9277-9277/? D/Cur: FirstActivity onResume
    // 第一次打开B
    12-03 08:02:10.000 9277-9277/com.amqr.taskstack D/Cur: FirstActivity onPause
    12-03 08:02:10.024 9277-9277/com.amqr.taskstack D/Cur: SecondActivity onCreate
    12-03 08:02:10.024 9277-9277/com.amqr.taskstack D/Cur: SecondActivity onStart
    12-03 08:02:10.024 9277-9277/com.amqr.taskstack D/Cur: SecondActivity onResume
    12-03 08:02:10.436 9277-9277/com.amqr.taskstack D/Cur: FirstActivity onStop
    
    // 第二次打开B 生命周期明显发生变化,只剩下 onRause和onResume
    12-03 08:02:11.720 9277-9277/com.amqr.taskstack D/Cur: SecondActivity onPause
    12-03 08:02:11.724 9277-9277/com.amqr.taskstack D/Cur: SecondActivity onNewIntent
    12-03 08:02:11.724 9277-9277/com.amqr.taskstack D/Cur: SecondActivity onResume
    
    
    • 情况3 ,打开A,打开B,打开C,再打开B

    这时虽然B是singleTop,但是由于打开A,打开B,打开C 的步骤走完任务栈里面的顺序是CBA,C是栈顶,因为B不处于栈顶所以不会复用B,此时还是会产生新的B实例进行入栈。

    有请终端先生:

    
        Running activities (most recent first):
          TaskRecord{52a15e50 #23 A=com.amqr.taskstack U=0 sz=4}
            Run #3: ActivityRecord{529d9310 u0 com.amqr.taskstack/.SecondActivity t23}
            Run #2: ActivityRecord{529d1b64 u0 com.amqr.taskstack/.ThirdActivity t23}
            Run #1: ActivityRecord{5297e728 u0 com.amqr.taskstack/.SecondActivity t23}
            Run #0: ActivityRecord{5297b8fc u0 com.amqr.taskstack/.FirstActivity t23}
    
    

    从上面的终端信息我们知道,B真的还是产生新的实例,没有复用,只要B不在顶部,特技就发挥不出来。

    生命周期也是没有什么特别的

    12-03 08:10:24.080 11761-11761/com.amqr.taskstack D/Cur: FirstActicity onCreate
    12-03 08:10:24.080 11761-11761/com.amqr.taskstack D/Cur: FirstActivity onStart
    12-03 08:10:24.080 11761-11761/com.amqr.taskstack D/Cur: FirstActivity onResume
    // 第一次打开B
    12-03 08:10:26.888 11761-11761/com.amqr.taskstack D/Cur: FirstActivity onPause
    12-03 08:10:26.908 11761-11761/com.amqr.taskstack D/Cur: SecondActivity onCreate
    12-03 08:10:26.908 11761-11761/com.amqr.taskstack D/Cur: SecondActivity onStart
    12-03 08:10:26.908 11761-11761/com.amqr.taskstack D/Cur: SecondActivity onResume
    12-03 08:10:27.340 11761-11761/com.amqr.taskstack D/Cur: FirstActivity onStop
    
    // 打开C
    12-03 08:10:27.880 11761-11761/com.amqr.taskstack D/Cur: SecondActivity onPause
    12-03 08:10:27.888 11761-11761/com.amqr.taskstack D/Cur: ThirdActivity onCreate
    12-03 08:10:27.888 11761-11761/com.amqr.taskstack D/Cur: ThirdActivity onStart
    12-03 08:10:27.888 11761-11761/com.amqr.taskstack D/Cur: ThirdActivity onResume
    12-03 08:10:28.300 11761-11761/com.amqr.taskstack D/Cur: SecondActivity onStop
    
    // 第二次打开B,
    12-03 08:10:29.040 11761-11761/com.amqr.taskstack D/Cur: ThirdActivity onPause
    12-03 08:10:29.056 11761-11761/com.amqr.taskstack D/Cur: SecondActivity onCreate
    12-03 08:10:29.056 11761-11761/com.amqr.taskstack D/Cur: SecondActivity onStart
    12-03 08:10:29.056 11761-11761/com.amqr.taskstack D/Cur: SecondActivity onResume
    12-03 08:10:29.436 11761-11761/com.amqr.taskstack D/Cur: ThirdActivity onStop
    
    

    .
    .

    4.3、singleTask 单一任务 (整个任务栈只有一个对应自身的实例)

    结论:如果开启的甲Activity已经存在一个实例在任务栈S1,再去开启这个Activity,位于栈顶则直接复用,回调onNewIntent方法;位于里面,也是复用,回调onNewIntent方法,复用的同时的是直接把自己上方的全部Activity都干掉。

    当我们把一个Activity设置为singleTask模式之后,当我们点击开启这个Activity,会出现3种情况:

    说明:打开B,A和C是Standard,B是singleTask

    之前没开启过:A开启B的时候,B进入A的任务栈。为了顶部

    之前开启过情况1:如果现在任务栈情况是BA,B位于栈顶,此时点击B,那么不会创建新的实例,任务栈还是BA,回调onNewIntent方法。

    之前开启过情况2:假如现在任务栈情况是CBA,C为了栈顶,那么这时打开B,因为B是singleTask,这时不会创建新的实例,但是肯定会把B置为栈顶(B在回到栈顶的时候不是跳过去的,而是把自己上面的其他Activity全部干掉,这样就只剩下自己和自己下面的Activity了),那么这时任务栈里面的情况就剩下 BA,回调onNewIntent方法.

    .
    .

    情景实测

    好啦,情况说完了,现在应该进行实测了。
    原先的代码不用改,只需要把SecondActivity指定为singleTask模式,我们这里采用manifes指定

            <activity android:name=".SecondActivity"
                android:launchMode="singleTask"
                />
    

    以下讨论的都是A和C都为Standard,B为singleTask

    • 情况1,打开A,打开B,在打开B。
      最终任务栈的结果是 BA. 其中B是栈顶,而且B只有一个,A是栈底。此时onNewIntent方法会被回调。

    这个演示需要有一个图片,明确清楚地知道我们第二次打开B的时候是打不开,没反应的。

    singleTask只有一个实例.gif

    通过上图,我们知道第二次点击“开启第2个Activity”是没有办法再次打开一个已经采用singleTask而且已经位于顶部的Activity新的实例。因为他就是那里,不会新增一个新的实例。(可以跟Standard的 情况3 做比较)

    终端的数据可以证明一切:

     Running activities (most recent first):
          TaskRecord{529f16ec #2 A=com.amqr.taskstack U=0 sz=2}
            Run #1: ActivityRecord{52a52380 u0 com.amqr.taskstack/.SecondActivity t2}
            Run #0: ActivityRecord{529bc564 u0 com.amqr.taskstack/.FirstActivity t2}
    
    

    查看下面的log,对比生命周期。
    这里跟单一顶部一样的效果。

    12-03 07:11:22.024 11795-11795/com.amqr.taskstack D/Cur: FirstActicity onCreate
    12-03 07:11:22.024 11795-11795/com.amqr.taskstack D/Cur: FirstActivity onStart
    12-03 07:11:22.024 11795-11795/com.amqr.taskstack D/Cur: FirstActivity onResume
    第一次按下打开B
    12-03 07:11:30.980 11795-11795/com.amqr.taskstack D/Cur: FirstActivity onPause
    12-03 07:11:30.992 11795-11795/com.amqr.taskstack D/Cur: SecondActivity onCreate
    12-03 07:11:30.992 11795-11795/com.amqr.taskstack D/Cur: SecondActivity onStart
    12-03 07:11:30.992 11795-11795/com.amqr.taskstack D/Cur: SecondActivity onResume
    12-03 07:11:31.384 11795-11795/com.amqr.taskstack D/Cur: FirstActivity onStop
    第二次按下打开B
    12-03 07:11:48.280 11795-11795/com.amqr.taskstack D/Cur: SecondActivity onPause
    12-03 07:11:48.280 11795-11795/com.amqr.taskstack D/Cur: SecondActivity onNewIntent
    12-03 07:11:48.280 11795-11795/com.amqr.taskstack D/Cur: SecondActivity onResume
    
    
    • 情况2 打开A,打开B,打开C,打开D,再次打开B。
      任务栈最终的情况 BA
      这个情况有意思,当我们第二次打开B的时候,他会把自己上方的全部Activity给干掉,最后只剩下自己和他身下的Activity了。
      说到底也正常,栈的结构看数据结构就知道了,其实也可以联想到现实,子弹夹我们想把倒数第二的子弹打出来,自然要把他前面的两个子弹给清掉。

    分两步来吧看个究竟吧
    当我们打开A,打开B,打开C,打开D,任务栈里面的情况是这样滴

    召唤终端大哥

        Running activities (most recent first):
          TaskRecord{52a36d80 #11 A=com.amqr.taskstack U=0 sz=4}
            Run #3: ActivityRecord{529be7bc u0 com.amqr.taskstack/.FourtActivity t11}
            Run #2: ActivityRecord{5295bdf8 u0 com.amqr.taskstack/.ThirdActivity t11}
            Run #1: ActivityRecord{5294692c u0 com.amqr.taskstack/.SecondActivity t11}
            Run #0: ActivityRecord{52a5f5f8 u0 com.amqr.taskstack/.FirstActivity t11}
    
    

    就在这时,任务栈的顺序是:DCBA ,其中D是栈顶。
    这时我们按下打开B,倒数第二的B 发射出一道闪闪金光,把上方的C和D全歼了,这时任务栈的顺序就只剩下BA了,B为栈顶,好残忍的说。

    然后再次召唤终端出场

        Running activities (most recent first):
          TaskRecord{52a15cdc #12 A=com.amqr.taskstack U=0 sz=2}
            Run #1: ActivityRecord{5294692c u0 com.amqr.taskstack/.SecondActivity t12}
            Run #0: ActivityRecord{52a66cc8 u0 com.amqr.taskstack/.FirstActivity t12}
    
    

    这里在查看一下生命周期的log,发现D和C付出了血的代价,直接被onDestroy了。

    12-03 07:24:16.536 23760-23760/? D/Cur: FirstActicity onCreate
    12-03 07:24:16.536 23760-23760/? D/Cur: FirstActivity onStart
    12-03 07:24:16.536 23760-23760/? D/Cur: FirstActivity onResume
    
    12-03 07:24:20.072 23760-23760/com.amqr.taskstack D/Cur: FirstActivity onPause
    12-03 07:24:20.088 23760-23760/com.amqr.taskstack D/Cur: SecondActivity onCreate
    12-03 07:24:20.088 23760-23760/com.amqr.taskstack D/Cur: SecondActivity onStart
    12-03 07:24:20.088 23760-23760/com.amqr.taskstack D/Cur: SecondActivity onResume
    12-03 07:24:20.484 23760-23760/com.amqr.taskstack D/Cur: FirstActivity onStop
    
    12-03 07:24:20.940 23760-23760/com.amqr.taskstack D/Cur: SecondActivity onPause
    12-03 07:24:20.956 23760-23760/com.amqr.taskstack D/Cur: ThirdActivity onCreate
    12-03 07:24:20.956 23760-23760/com.amqr.taskstack D/Cur: ThirdActivity onStart
    12-03 07:24:20.956 23760-23760/com.amqr.taskstack D/Cur: ThirdActivity onResume
    12-03 07:24:21.352 23760-23760/com.amqr.taskstack D/Cur: SecondActivity onStop
    
    12-03 07:24:21.708 23760-23760/com.amqr.taskstack D/Cur: ThirdActivity onPause
    12-03 07:24:21.720 23760-23760/com.amqr.taskstack D/Cur: FourtActivity onCreate
    12-03 07:24:21.720 23760-23760/com.amqr.taskstack D/Cur: FourtActivity onStart
    12-03 07:24:21.720 23760-23760/com.amqr.taskstack D/Cur: FourtActivity onResume
    12-03 07:24:22.128 23760-23760/com.amqr.taskstack D/Cur: ThirdActivity onStop
    12-03 07:24:24.400 23760-23760/com.amqr.taskstack D/Cur: ThirdActivity onDestroy
    
    12-03 07:24:24.404 23760-23760/com.amqr.taskstack D/Cur: FourtActivity onPause
    12-03 07:24:24.408 23760-23760/com.amqr.taskstack D/Cur: SecondActivity onNewIntent
    12-03 07:24:24.408 23760-23760/com.amqr.taskstack D/Cur: SecondActivity onStart
    12-03 07:24:24.408 23760-23760/com.amqr.taskstack D/Cur: SecondActivity onResume
    12-03 07:24:24.784 23760-23760/com.amqr.taskstack D/Cur: FourtActivity onStop
    12-03 07:24:24.784 23760-23760/com.amqr.taskstack D/Cur: FourtActivity onDestroy
    
    

    singleTask的谈论至此完毕。

    .
    .

    4.4 singleInstance 单一实例(单例),任务栈里面自已自己一个人

    结论:当启动一个启动模式为singleInstance的Activity时(之前没启动过),这时系统将开辟出另外一个任务栈,用于存放这个Activity,而且这个新的任务栈只能存放自身这唯一一个Activity。singleInstance页面作为前台任务打开自己打开自己,则复用,任务栈顺序无变化;singleInstance页面作为后台任务栈,则切换成为前台任务栈,无新实例产生,复用。

    复用就会调用onNewIntent方法。

    情景实测

    以下的A和B都是Standard,然后C和D我们都通manifest把启动模式设定为 singleInstance

            <activity android:name=".ThirdActivity"
    
                android:launchMode="singleInstance"
                />
            <activity android:name=".FourtActivity"
                android:launchMode="singleInstance"
                />
    
    • 情景1:打开A,打开B,打开为singleInstance的C
      打开A,A存在于任务栈S1,此时任务栈里面只有A,
      打开B, B入栈,进入S1,此时S1的里面有两个Activity,顺序为BA,B为栈顶。

    打开C,因为C是singleInstance,所以自己的任务栈。很明显,这个时候C不会进入S1的。而是会开辟出来一个全新的任务栈,我们就称为任务栈S2吧,
    注意,此时S2属于前台任务栈,S1属于后台任务栈。

    如果非要排个需要序号的话,那么就是 (S2-C)、(S1-B)、(S1-A),前面的是老大。

    请出终端大哥:

        Running activities (most recent first):
          TaskRecord{52a54d24 #31 A=com.amqr.taskstack U=0 sz=1}
            Run #2: ActivityRecord{5297e728 u0 com.amqr.taskstack/.ThirdActivity t31}
          TaskRecord{52a193f4 #30 A=com.amqr.taskstack U=0 sz=2}
            Run #1: ActivityRecord{5297b8fc u0 com.amqr.taskstack/.SecondActivity t30}
            Run #0: ActivityRecord{52a5f120 u0 com.amqr.taskstack/.FirstActivity t30}
    
    

    别只看A="aaa.bbb.ccc"相同, 注意,#num ,比如#31和#30就不同就代表是两个不同任务栈。#号带的数字是最能区分的标志。我们说的 # 是紧挨着任务栈名称左边的那个#。

    生命周期没什么影响

    12-03 08:32:27.108 24263-24263/com.amqr.taskstack D/Cur: FirstActicity onCreate
    12-03 08:32:27.108 24263-24263/com.amqr.taskstack D/Cur: FirstActivity onStart
    12-03 08:32:27.108 24263-24263/com.amqr.taskstack D/Cur: FirstActivity onResume
    // 打开B
    12-03 08:32:28.828 24263-24263/com.amqr.taskstack D/Cur: FirstActivity onPause
    12-03 08:32:28.844 24263-24263/com.amqr.taskstack D/Cur: SecondActivity onCreate
    12-03 08:32:28.844 24263-24263/com.amqr.taskstack D/Cur: SecondActivity onStart
    12-03 08:32:28.844 24263-24263/com.amqr.taskstack D/Cur: SecondActivity onResume
    12-03 08:32:29.236 24263-24263/com.amqr.taskstack D/Cur: FirstActivity onStop
    // 打开为 singleInstance 的C
    12-03 08:32:29.888 24263-24263/com.amqr.taskstack D/Cur: SecondActivity onPause
    12-03 08:32:29.892 24263-24263/com.amqr.taskstack D/Cur: ThirdActivity onCreate
    12-03 08:32:29.892 24263-24263/com.amqr.taskstack D/Cur: ThirdActivity onStart
    12-03 08:32:29.892 24263-24263/com.amqr.taskstack D/Cur: ThirdActivity onResume
    12-03 08:32:30.656 24263-24263/com.amqr.taskstack D/Cur: SecondActivity onStop
    
    
    • 情景2、打开A,打开B,打开为singleInstance的C,然后按下返回键,第二次按返回键,第三次再按下返回键。

    也就是在情景1的条件下再按下返回键。

    我们再续前缘接着说。
    第一次按下返回键,S2销毁,S1成为前台任务栈,S1任务栈里面的顺序是BA,其中B是栈顶。
    界面停留在B界面。

    第二次按下返回键,界面退到A界面,S1任务里面B出栈,任务栈里面只剩A。

    第三次按下返回键,整个程序退出,任务栈S1销毁。

    • 情景3,打开A,打开B,打开singleInstance的C,C打开C(复用,无新实例),接着打开singleInstance的D,接着D打开C。(复用,无新实例,改变任务栈顺序)
      C和D都复写onNewIntent方法以便观察。

    那么这时会产生3个任务栈,A和B所在的为S1,C所在的任务栈为S2,D所所在的任务栈为S3.

    步骤一:
    打开A,打开B,B打开的C
    终端

        Running activities (most recent first):
          TaskRecord{529819f4 #44 A=com.amqr.taskstack U=0 sz=1}
            Run #2: ActivityRecord{529cfc00 u0 com.amqr.taskstack/.ThirdActivity t44}
          TaskRecord{52a05480 #43 A=com.amqr.taskstack U=0 sz=2}
            Run #1: ActivityRecord{529c2564 u0 com.amqr.taskstack/.SecondActivity t43}
            Run #0: ActivityRecord{529a6e24 u0 com.amqr.taskstack/.FirstActivity t43}
    
    
    

    生命周期

    12-05 23:10:11.178 15891-15891/? D/Cur: FirstActicity onCreate
    12-05 23:10:11.178 15891-15891/? D/Cur: FirstActivity onStart
    12-05 23:10:11.178 15891-15891/? D/Cur: FirstActivity onResume
    // 打开B
    12-05 23:10:16.166 15891-15891/com.amqr.taskstack D/Cur: FirstActivity onPause
    12-05 23:10:16.174 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onCreate
    12-05 23:10:16.174 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onStart
    12-05 23:10:16.174 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onResume
    12-05 23:10:16.558 15891-15891/com.amqr.taskstack D/Cur: FirstActivity onStop
    // 第一次打开C
    12-05 23:10:17.826 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onPause
    12-05 23:10:17.826 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onCreate
    12-05 23:10:17.826 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onStart
    12-05 23:10:17.826 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onResume
    12-05 23:10:18.622 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onStop
    
    

    .
    .
    步骤2:再次打开C,复用,终端信息一致。就不附上了。
    生命周期有所不同。

    12-05 23:10:11.178 15891-15891/? D/Cur: FirstActicity onCreate
    12-05 23:10:11.178 15891-15891/? D/Cur: FirstActivity onStart
    12-05 23:10:11.178 15891-15891/? D/Cur: FirstActivity onResume
    // 打开B
    12-05 23:10:16.166 15891-15891/com.amqr.taskstack D/Cur: FirstActivity onPause
    12-05 23:10:16.174 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onCreate
    12-05 23:10:16.174 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onStart
    12-05 23:10:16.174 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onResume
    12-05 23:10:16.558 15891-15891/com.amqr.taskstack D/Cur: FirstActivity onStop
    // 第一次打开C
    12-05 23:10:17.826 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onPause
    12-05 23:10:17.826 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onCreate
    12-05 23:10:17.826 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onStart
    12-05 23:10:17.826 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onResume
    12-05 23:10:18.622 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onStop
    // 第二次打开C
    12-05 23:10:20.650 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onPause
    12-05 23:10:20.650 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onNewIntent
    12-05 23:10:20.650 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onResume
    
    

    步骤3:C打开D

        Running activities (most recent first):
          TaskRecord{529f9160 #45 A=com.amqr.taskstack U=0 sz=1}
            Run #3: ActivityRecord{529f8ff8 u0 com.amqr.taskstack/.FourtActivity t45}
          TaskRecord{529819f4 #44 A=com.amqr.taskstack U=0 sz=1}
            Run #2: ActivityRecord{529cfc00 u0 com.amqr.taskstack/.ThirdActivity t44}
          TaskRecord{52a05480 #43 A=com.amqr.taskstack U=0 sz=2}
            Run #1: ActivityRecord{529c2564 u0 com.amqr.taskstack/.SecondActivity t43}
            Run #0: ActivityRecord{529a6e24 u0 com.amqr.taskstack/.FirstActivity t43}
    
    

    生命周期

    接着上次的LOG
    
    // 第二次打开C
    12-05 23:10:20.650 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onPause
    12-05 23:10:20.650 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onNewIntent
    12-05 23:10:20.650 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onResume
    // C打开D
    12-05 23:32:29.242 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onPause
    12-05 23:32:29.246 15891-15891/com.amqr.taskstack D/Cur: FourtActivity onCreate
    12-05 23:32:29.246 15891-15891/com.amqr.taskstack D/Cur: FourtActivity onStart
    12-05 23:32:29.246 15891-15891/com.amqr.taskstack D/Cur: FourtActivity onResume
    12-05 23:32:30.038 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onStop
    
    

    发现多了一个任务栈。

    步骤4 D打开C

    终端

        Running activities (most recent first):
          TaskRecord{529819f4 #44 A=com.amqr.taskstack U=0 sz=1}
            Run #3: ActivityRecord{529cfc00 u0 com.amqr.taskstack/.ThirdActivity t44}
          TaskRecord{529f9160 #45 A=com.amqr.taskstack U=0 sz=1}
            Run #2: ActivityRecord{529f8ff8 u0 com.amqr.taskstack/.FourtActivity t45}
          TaskRecord{52a05480 #43 A=com.amqr.taskstack U=0 sz=2}
            Run #1: ActivityRecord{529c2564 u0 com.amqr.taskstack/.SecondActivity t43}
            Run #0: ActivityRecord{529a6e24 u0 com.amqr.taskstack/.FirstActivity t43}
    
    

    生命周期

    接上次的C打开D的Log
    // C打开D
    12-05 23:32:29.242 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onPause
    12-05 23:32:29.246 15891-15891/com.amqr.taskstack D/Cur: FourtActivity onCreate
    12-05 23:32:29.246 15891-15891/com.amqr.taskstack D/Cur: FourtActivity onStart
    12-05 23:32:29.246 15891-15891/com.amqr.taskstack D/Cur: FourtActivity onResume
    12-05 23:32:30.038 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onStop
    
    
    // D打开C
    12-05 23:56:10.402 15891-15891/com.amqr.taskstack D/Cur: FourtActivity onPause
    12-05 23:56:10.402 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onNewIntent
    12-05 23:56:10.402 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onStart
    12-05 23:56:10.402 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onResume
    12-05 23:56:11.154 15891-15891/com.amqr.taskstack D/Cur: FourtActivity onStop
    

    没有新的任务栈产生,前台任务栈发了变化。任务栈里面也不会有新的Activity产生。

    singleInstance分析至此结束。

    5、另说singleTask的小伙伴:taskAffinity + allowTaskReparenting

    说在前面

    taskAffinity是singleTask的好伙伴,这是肯定的。

    按照官网大概是这么说taskAffinity 和 allowTaskReparenting的:
    taskAffinity: 可以指定一个Activity放入哪一个任务栈中,利用taskAffinity制定一个任务栈的名称,把Activity放进这个任务栈中。实现这个过程需要singleTask和allowTaskReparenting两者的协助。

    allowTaskReparenting:参数是boolean,如果我们利用taskAffinity让Activity放入一个指定的任务栈,需要allowTaskReparenting的同意,为true就可以跟着别人跑,为false就乖乖在原地呆着那都不许去。

    按照的官网的说法,实测后发现,taskAffinity确实可以让Activity跑到指定的任务栈(不用跟app自带的任务栈混了),但是allowTaskReparenting没什么作用,设置或者不设置没什么改变。设置为true,设置为false,或者不设置,一点都没区别。

    即是说官方说,taskAffinity要和allowTaskReparenting配合着使用,实测是上不用,taskAffinity单兵作战也是可以的。(个人看法,疏忽之处请熟悉的朋友指点一下)

    下面开始正式分析。

    5.1、taskAffinity

    taskAffinity属性用于给Activity单独指定任务栈名称。这个名称不能和包名相同,否则就没有意义。注意taskAffinity属性值为String,而且中间必须包含有分隔符 . (英文状态下的点),比如com.baidu.test

    另外,如果想要指定一个非包名的任务栈,该Activity一定要把启动模式设置为singleTask模式,否则不会生效。如果taskAffinity指定的名称是其他程序的包名,那么可以不结合singleTask。念起来好像有点拗口,看下面的实测就知道怎么回事了。

    注意:任务栈分为前台任务栈和后台任务栈,后台任务栈里面的Activity全部处于onStop状态。

    在minifest里面,application可以设定taskAffinity,activity也可以设定taskAffinity。
    taskAffinity设定的任务栈我们也称其为一个宿主任务栈。

    • application设定

      • applicatipn如果不设定,那么就系统默认设定为包名。如果设定了,activity跟着application走,application指定的是什么activity的任务栈的名称就是什么。(application自带的不设定,一般我们也不手动设定,要设定也是单独在activity里面设定)
    • activity设定

      • 设定taskAffinity之后,当启动这个Activity之后,如果之前没有任务栈的存在,那么就启动系统会开辟出来一个新的任务栈(或者叫宿主任务栈),用于存放这个activity,如果已经存在了这个任务栈,那么这个activity就对应进入已经的宿主任务栈。(设定taskAffinity,不管之前存不存在,反正就不跟默认的混了,自己只认指定的任务栈,当然,如果你非要把taskAffinity指定自己的包名那也没办法,只是那没撒意思嘛)

    我们利用taskAffinity+taskAffinity指定非手机里面任何程序的包名的任务栈时,这个任务是可以容纳多个activity的。比如现在有A,B,C三个界面,B和C都启动模式都是taskAffinity,而且taskAffinity指定的都是同一个包名。
    那么当我们A开启B,B再开启C的时候,结果就是A在app默认自带的任务栈S1里,而B和C在同一个任务栈S2里面。这是S2里面的顺序是CB,C是栈顶。然后这个时候从C打开B,那么事情来了,本来的C下面的B会再次汇聚天地能量,发出一道闪闪金光,把自己上方的所有Activity全部歼灭,这个时候C就挂了。可见,不管和谁合作,singleTask依旧霸道,不可阻挡,谁挡谁卒。

    有代码有真相,先来看一下如何利用taskAffinity给activty指定任务栈:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.amqr.newtaskone" >
    
        <activity android:name=".OtherActivity"
    // android:taskAffinity 的使用一般都是有singeTask一起出现的
            android:taskAffinity="com.task.try"
    
            />
    

    如上,我们的包名是 com.amqr.newtaskone,然后我们给我们的activity指定了宿主任务栈的的名称为 com.task.try 。这样他就和系统默认的任务栈名称不同了。

    5.2、allowTaskReparenting

    allowTaskReparenting:参数是boolean。
    如果我们利用taskAffinity让Activity放入一个指定的任务栈,需要allowTaskReparenting的同意,为true就可以跟着别人跑,为false就乖乖在原地呆着除了自己家那都不许去。

    怎么用

            <activity android:name=".OtherActivity"
    
                android:taskAffinity="com.task.try"
                android:allowTaskReparenting="true"
    
                />
    

    5.3 实测

    情况1和情况2,app2的activity指定的任务栈名称是app1的包名,所以不需要singleTask(这里指定包名是为了演示效果)

    • 情况1:新建两个app。
      app1什么都不改,自带一个MainActivity(布局文件标识一下是app1的Mainactivity)。
      在app2的manifest给app2的MainAcivity添加android:taskAffinity属性,指定包名为 app1的包名。

    也就是说,当app2的MainActivity的启动的时候会把app的默认任务栈当做自己的宿主。

    我们把两个程序安装一下,清掉其他正在运行的程序
    运行app1,然后按下home键(让app1变成后台程序),接着打开app2,我们会发现,这时候进入的不是app2的界面,而是app1的。

    先来看一下两个app的Mainactivty本来的面目:

    app1的MainActivityUI截图

    app1的MainActivityUI截图.png

    app2的MainActivityUI截图

    app2的MainActivityUI截图.png

    接下来看一下各自的manifest

    app1的manifest

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.amqr.taskaffinity1" >
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme"
            >
            <activity android:name=".MainActivity1">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    
    

    app2的manifest

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.amqr.taskaffinity2" >
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme" >
            <activity android:name=".MainActivity2"
                android:taskAffinity="com.amqr.taskaffinity1"
                >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    
    

    查看效果:

    宿主.gif

    终端大哥也显示为只有一个任务栈,那就是app1自带的那个com.amqr.taskaffinity1

        Running activities (most recent first):
          TaskRecord{5296a83c #4 A=com.amqr.taskaffinity1 U=0 sz=1}
            Run #0: ActivityRecord{52983d74 u0 com.amqr.taskaffinity1/.MainActivity1 t4}
    
    

    生命周期

    12-03 22:50:46.324 1835-1835/com.amqr.taskaffinity1 D/Cur: MainActivity1, onCreate
    12-03 22:50:46.324 1835-1835/com.amqr.taskaffinity1 D/Cur: MainActivity1, onStart
    12-03 22:50:46.324 1835-1835/com.amqr.taskaffinity1 D/Cur: MainActivity1, onResume
    // 按下home键
    12-03 22:50:48.356 1835-1835/com.amqr.taskaffinity1 D/Cur: MainActivity1, onPause
    12-03 22:50:48.872 1835-1835/com.amqr.taskaffinity1 D/Cur: MainActivity1, onStop
    // 启动app2,结果打开是app1
    12-03 22:50:52.868 1835-1835/com.amqr.taskaffinity1 D/Cur: MainActivity1, onRestart
    12-03 22:50:52.868 1835-1835/com.amqr.taskaffinity1 D/Cur: MainActivity1, onStart
    12-03 22:50:52.868 1835-1835/com.amqr.taskaffinity1 D/Cur: MainActivity1, onResume
    
    

    我们发现,我们点击启动的app2,结果启动的却是app1,而且,在运行列表里面看不到app2的存在,只有一个app1。

    里面发生的是这样的一个过程,
    当我们启动app1时:app1的任务栈com.amqr.taskaffinity1 正常作为一个任务栈进入系统,
    按下home键时:任务栈 com.amqr.taskaffinity1 成为了一个后台任务栈
    点击app2时: 本来应该打开的是app2的界面,但是要打开的MainActivity2发现自身含有android:taskAffinity,而指定的宿主任务栈就是app1打开后就存在的任务栈 com.amqr.taskaffinity1 ,那么这时候 com.amqr.taskaffinity1 就成为了 MainActivity2 的宿主任务栈。
    按道理,这个时候本来应该是 MainActivity2 入栈而且作为栈顶的啊,但是事实却没有这么发生。也就是说,这个时候,MainActivity2没有入栈,对的,没有入栈。他是跑过来搞笑的,通知了一下别人可以变成前台任务栈了,然后自己没有入栈,具体详情,不得而知。
    实际上发生的事情是:com.amqr.taskaffinity1 由后台任务栈转为前台任务栈,而那个MainActity没有入栈,从终端的信息就可以看得出来。

    有人说,你没按谷歌的来,没有把allowTaskReparenting设置为true,好,那我们进行其概况2,接着来一遍。

    • 情况2 还是上面的代码,我们只是app2的manifest里面给MainActivity加上一句

    android:allowTaskReparenting="true"

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.amqr.taskaffinity2" >
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme" >
            <activity android:name=".MainActivity2"
                android:taskAffinity="com.amqr.taskaffinity1"
                android:allowTaskReparenting="true"
                >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    
    

    我们重新安装两个程序,接着来一遍。
    (清除所有运行的程序,打开app1,按下home,打开app2)

    宿主情况2.gif

    结果发现一样,还是贴一下对应的终端信息吧

        Running activities (most recent first):
          TaskRecord{52992bc4 #10 A=com.amqr.taskaffinity1 U=0 sz=1}
            Run #0: ActivityRecord{52a97bdc u0 com.amqr.taskaffinity1/.MainActivity1 t10}
    
    

    一个样,其实就算设置为false也是没有变化,有兴趣的可以自己试一下。

    注意:宿主不是绝对化的,当两个app都认定一个宿主后,�就先来后到了,谁先开启那个任务栈谁是老大。比如说,我们在可以清掉所有运行的程序,先开启app2,然后再开启app1,我们会发现打开的是app2,就不是app1了。

    app2宿主.gif
    • 情况三:
      情况三不是指定程序的包名作为指定的任务栈名称,所以需要启动的模式singleTask一起工作。(如果不跟singleTask合作,又不指定为程序的包名,那么设置android:taskAffinity不生效)

    情况三我们这样弄,app1和app2各自都有两个页面,各自的启动页都是普通常见的,然后各自的都有另外一个其他页面,这分属于两个app的其他页面我们都给他们指定同一个任务栈名(这个任务站名不跟任意一个app包名相同)

    也就是说,app1里面有MainActivity1和App1Other两个Activity,
    app2里面有MainActivity2和App2Other两个Activity

    MainActivity1和MainActivity2都是普通的Standard
    而App1Other和App2Other都在清单文件指定了自定义的任务栈名称和singelTask启动模式。详见下方代码

    我们这样操作:
    第一阶段:先打开app1,再打开App1Other页面,接着按下home键
    第二阶段:(接按下home件之后)打开app2,打开app2的App2Other页面

    先贴上相关图片和代码先:

    Paste_Image.png Paste_Image.png Paste_Image.png Paste_Image.png

    app1的manifest

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.amqr.taskaffinity1" >
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme"
            >
            <activity android:name=".MainActivity1">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
            <activity android:name=".App1Other"
    
                android:taskAffinity="com.amqr.independent"
                android:launchMode="singleTask"
    
    
                />
        </application>
    
    </manifest>
    
    

    .
    .
    app2的manifest

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.amqr.taskaffinity2" >
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme" >
            <activity android:name=".MainActivity2"
                >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
            <activity android:name=".App2Other"
                android:taskAffinity="com.amqr.independent"
                android:launchMode="singleTask"
                />
        </application>
    
    </manifest>
    
    
    • 第一阶段,
      我们先打开app1,然后打开的app1Other界面。按下home键

    效果如下图:打开第一个activity,然后再点击打开app1Other,按下home键盘,查看运行列表,会看见变成了两个任务栈(任务栈是两个,程序还是一个,进程更加只是一个)


    taskAffinity1.gif

    任务栈信息

    
    
        Running activities (most recent first):
          TaskRecord{52a8eab0 #48 A=com.amqr.independent U=0 sz=1}
            Run #1: ActivityRecord{52ae1518 u0 com.amqr.taskaffinity1/.App1Other t48}
          TaskRecord{52a9cd20 #47 A=com.amqr.taskaffinity1 U=0 sz=1}
            Run #0: ActivityRecord{52a9f230 u0 com.amqr.taskaffinity1/.MainActivity1 t47}
    
    
      Recent tasks:
      * Recent #0: TaskRecord{52973240 #52 A=com.amqr.independent U=0 sz=1}
      * Recent #1: TaskRecord{52979df8 #51 A=com.amqr.taskaffinity1 U=0 sz=1}
      * Recent #2: TaskRecord{5295e6f8 #1 A=com.android.launcher U=0 sz=1}
      * Recent #3: TaskRecord{52a90f20 #46 A=com.android.systemui U=0 sz=0}
    
    

    生命周期

    12-04 01:53:51.808 18259-18259/? D/Cur: MainActivity1, onCreate
    12-04 01:53:51.808 18259-18259/? D/Cur: MainActivity1, onStart
    12-04 01:53:51.808 18259-18259/? D/Cur: MainActivity1, onResume
    
    12-04 01:53:55.704 18259-18259/com.amqr.taskaffinity1 D/Cur: MainActivity1, onPause
    12-04 01:53:55.708 18259-18259/com.amqr.taskaffinity1 D/Cur: App1Other, onCreate
    12-04 01:53:55.708 18259-18259/com.amqr.taskaffinity1 D/Cur: App1Other, onStart
    12-04 01:53:55.708 18259-18259/com.amqr.taskaffinity1 D/Cur: App1Other, onResume
    12-04 01:53:56.468 18259-18259/com.amqr.taskaffinity1 D/Cur: MainActivity1, onStop
    
    
    

    从上面的图文我们知道,
    当我们点击app1时,默认的用的是系统的根据包名定的默认任务栈,这个没什么可说的。

    但是当我们点击打开 app1Other 页面时,就多出来一个任务栈,栈名是我们指定的com.amqr.independent。

    而且观察运行程序的列表,发现多了个图标,但是要知道,这个绝对不是多了一个进程,只是我们每多产生多一个任务栈,任务列表就会多出来一个图标。

    一个程序的两个任务栈.png

    进程不变

    Paste_Image.png
    • 第二阶段

    紧接上面的环节
    接下来我们来打开app2,然后打开app2的 app2Other 界面
    (完整的操作:打开app1,打开app1Other界面,按下home,打开app2,打开app2的Other界面)

    终端信息

        Running activities (most recent first):
          TaskRecord{52a2e064 #62 A=com.amqr.independent U=0 sz=2}
            Run #3: ActivityRecord{529d1550 u0 com.amqr.taskaffinity2/.App2Other t62}
          TaskRecord{52a337a0 #63 A=com.amqr.taskaffinity2 U=0 sz=1}
            Run #2: ActivityRecord{5299485c u0 com.amqr.taskaffinity2/.MainActivity2 t63}
          TaskRecord{52a2e064 #62 A=com.amqr.independent U=0 sz=2}
            Run #1: ActivityRecord{529827a0 u0 com.amqr.taskaffinity1/.App1Other t62}
          TaskRecord{528a1e60 #61 A=com.amqr.taskaffinity1 U=0 sz=1}
            Run #0: ActivityRecord{5296bc68 u0 com.amqr.taskaffinity1/.MainActivity1 t61}
    
    

    程序截图


    Paste_Image.png

    我们发现了,
    图片的任务列表是3个,终端的日志任务栈看起来像出现了4个,其实是3个任务栈,因为有两个是重复的,为什么重复了呢?

    我们先来看一下这里面的两句:
    TaskRecord{52a2e064 #62 A=com.amqr.independent U=0 sz=2}
    TaskRecord{52a2e064 #62 A=com.amqr.independent U=0 sz=2}
    我们通过名字com.amqr.independent可以知道这肯定是同一个任务栈
    其实这个sz后面表示的就是activity的个数(已测)
    至于为什么分开显示,这个应该是终端显示的一种表示方法,终端的具体规则不太清楚。这里不是我们深究的方向。到了这里明白运行的是3个任务栈就好了。

    我们这里了明白singleTask + taskAffinity是如何使用的就好了。

    • 情况4

    app1里面弄3个activity,MainActivity,App1Other,App3Activity,分别用A,B,C指代,A是Standard的启动页,B和C都是single + taskAffinity且指定的任务栈名称相同(不跟任何程序包名相同)。

    操作过程,
    第一阶段:运行app1,启动页A启动B,

    第二阶段:
    再通过B启动C
    最后我们发现,在singleTask + taskAffinity模式下还是谁当灭灭谁,在它前面的全部遭殃。

    贴上代码和相关图片:

    manifest

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.amqr.taskaffinity1" >
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme"
            >
            <activity android:name=".MainActivity1">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
            <activity android:name=".App1Other"
    
                android:taskAffinity="com.amqr.independent"
                android:launchMode="singleTask"
    
    
                />
    
    
            <activity android:name=".App3Activity"
                
                android:taskAffinity="com.amqr.independent"
                android:launchMode="singleTask"
    
                />
        </application>
    
    </manifest>
    
    

    第一阶段:

    A开启B,B开启C,这时候我们的指定任务栈里面的顺序就是CB


    两个taskAffinity.gif

    第一阶段终端信息

        Running activities (most recent first):
          TaskRecord{52983174 #116 A=com.amqr.independent U=0 sz=2}
            Run #2: ActivityRecord{5290923c u0 com.amqr.taskaffinity1/.App3Activity t116}
            Run #1: ActivityRecord{52a80e58 u0 com.amqr.taskaffinity1/.App1Other t116}
          TaskRecord{5299bf74 #115 A=com.amqr.taskaffinity1 U=0 sz=1}
            Run #0: ActivityRecord{52a32b30 u0 com.amqr.taskaffinity1/.MainActivity1 t115}
    
    

    第一阶段结束。

    .
    .
    第二阶段:

    紧接着上面的操作,在上面的操作里面我们的指定的任务栈里面已经有了两个都是singleTask的任务在里面了,B在C的下面,C是栈顶,这个时候我们从C开启B,结果C卒,挂了。

    两个taskAffinity,消灭上方.gif

    第二阶段终端信息:

    
        Running activities (most recent first):
          TaskRecord{5295e6f8 #1 A=com.android.launcher U=0 sz=1}
            Run #1: ActivityRecord{5295cb64 u0 com.android.launcher/com.android.launcher2.Launcher t1}
          TaskRecord{52abf0cc #117 A=com.android.systemui U=0 sz=1}
            Run #0: ActivityRecord{5290923c u0 com.android.systemui/.recent.RecentsActivity t117}
    
    

    本篇就先说道这里吧,下次说标志位—— FLAG.

    对5.2情况3有兴趣的可以参考这里Android-Activity5.3情景3之完整Log参考
    完。

    相关文章

      网友评论

      • 那时花儿:楼主,最后的那张图是不是贴错了task?贴出来的task是关于launcher和system ui的
        阿敏其人:@那时花儿 是那个第二阶段终端信息吗,那个是任务栈啊
      • breakingbad:“第一阶段:

        A开启B,B开启C,这时候我们的指定任务栈里面的顺序就是BC”

        勘误:应该是CB
        阿敏其人:@breakingbad fix,谢谢
      • breakingbad:楼主,你对allowTaskReparenting的理解可能有点问题。
        allowTaskReparenting属性用于设定Activity能够从启动它的任务中转移到另一个有亲缘关系的任务中,转移时机是在这个有亲缘关系的任务被带到前台的时候。如果设置了true,则能够转移,如果设置了false,则这个Activity必须要保留在启动它的那个任务中。
        请注意,这里说的能否转移是说在这个有亲缘关系的任务被带到前台的时候能否转移。

        ”即是说官方说,taskAffinity要和allowTaskReparenting配合着使用,实测是上不用,taskAffinity单兵作战也是可以的。”你想的方向是打开一个设了 taskAffinity的Activity时该Activity所在的栈能否不一样,当然,这和allowTaskReparenting的值没有关系,这是对的。
        但是,allowTaskReparenting和taskAffinity配合使用也是对的。举个例子,有一个应用A,当前的栈为taskA,打开了应用b的一个页面WActivity,假设WActivity的taskAffinity没设置,allowTaskReparenting为true,那么WActivity就会在taskA的最上方,此时按home键,再点击应用b,那么因为allowTaskReparenting为true,而应用b的默认taskAffinity就是应用b的包名,WActivity的taskAffinity因为没设置所以也是b的包名,这时候应用b创建和包名同名的栈,WActivity就会转移到此栈中去,显示的就是WActivity。如果WActivity的taskAffinity设置了其他值,那么因为亲和值和默认包名不一样所以WActivity不会转移了,打开应用b时默认显示的就是b的主页。

        欢迎交流哈。
        breakingbad:@阿敏其人 效果还真不一样。像你举的这个例子,假设在应用A的F页面打开B应用的MainActivity,然后按home,再打开B,若allowTaskReparenting为true,则这个B应用的MainActivity实例会转移到B应用的任务栈中显示出来,而不是创建一个新的MainActivity实例。当再切换到A应用后,显示的是F页面,因为本来在A应用任务栈顶的MainActivity实例已经移到B应用的任务栈中去了,所以当前A应用任务栈的栈顶就会是F页面了;否则,若allowTaskReparenting为false,则B应用会重新创建一个新的MainActivity实例,如果再切换到A应用则发现显示的依然是之前打开的MainActivity。
        阿敏其人:@breakingbad 打错了一点,应该是应用A就是个普通的新建app,A打开B,应用B的MainActivity设置了taskAffinity无设置,allowTaskReparenting为true。那么顺序是打开A,按下Home,顺利打开B。然后如果allowTaskReparenting为false,效果一致。那么这样就和我们直接新建两个app直接分别运行然后依次打开没区别呀。
        阿敏其人:@breakingbad 嗯,你看这样子。现在你说的,应用A就是个普通的新建app,应用B设置了taskAffinity无设置,allowTaskReparenting为true,那么确实是打开A,按下Home,打开应用B的时候顺利打开应用B,此时有两个任务栈产生。但是,上面的条件保持不变,然后你把allowTaskReparenting置为false。产生的效果是一模一样的。这样我们设置allowTaskReparenting的作用体现在哪里呢。我们设置与不设置,跟我们直接新建两个程序什么都不该直接运行然后分别打开又有什么区别。 :grin:

      本文标题:Android-Activity所应该了解的大概就这样。(中)

      本文链接:https://www.haomeiwen.com/subject/ouyshttx.html