zoukankan      html  css  js  c++  java
  • TaskRecord分析

    gionee/alps/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

    taskRecord

    http://www.jianshu.com/p/c7889a533050

    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,这时候我们的指定任务栈里面的顺序就是BC


    两个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.

    在某些情况下,Android需要知道一个Activity属于哪个Task,即使它没有被启动到一个具体的Task里。这是通过任务共用性(Affinities)完成的。任务共用性(Affinities)为这个运行一个或多个Activity的Task提供了一个独特的静态名称,默认的一个活动的任务共用性(Affinity)是实现了该Activity的.apk包的名字。

           当开始一个没有Intent.FLAG_ACTIVITY_NEW_TASK标志的Activity时,任务共用性affinities不会影响将会运行该新活动的Task:它总是运行在启动它的Task里。但是,如果使用了NEW_TASK标志,那么共用性(affinity)将被用来判断是否已经存在一个有相同共用性(affinity)的Task。如果是这样,这项Task将被切换到前面而新的Activity会启动于这个Task的顶层。

           这种特性在您必须使用NEW_TASK标志的情况下最有用,尤其是从状态栏通知或桌面快捷方式启动活动时。结果是,当用户用这种方式启动您的应用程序时,它的当前Task将被切换到前台,而且想要查看的Activity被放在最上面。

           你可以在程序清单(Manifest)文件的应用程序application标签中为.apk包中所有的活动分配你自己的任务共用性Affinites,或者在活动标记中为各个活动进行分配。

  • 相关阅读:
    oracle基于事件的调度简单学习
    ALTER SEQUENCE
    初识oracle嵌套表
    关于索引失效的一点学习
    Oracle的job用法简单小结
    oracle中的FLASHBACK TABLE 和查询闪回的一点认识
    数据库事务的学习(二)
    电动车驱动力与行驶阻力平衡图.png
    matlab 等高线contour
    IPMSM弱磁控制策略
  • 原文地址:https://www.cnblogs.com/zhengtu2015/p/5166867.html
Copyright © 2011-2022 走看看