Android-Activity所應(yīng)該了解的大概就這樣。(中)

本文出自 “阿敏其人” 簡書博客,轉(zhuǎn)載或引用請注明出處。

Android-Activity所應(yīng)該了解的大概就這樣。(上)
Android-Activity所應(yīng)該了解的大概就這樣。(下)

五、任務(wù)棧/Activity的啟動模式

知道生命周期、線程的優(yōu)先級和Activity的異常銷毀,下面我們來認(rèn)識一下任務(wù)棧。

我們上面進(jìn)行的那么多描述和代碼,都是在standard這種默認(rèn)的任務(wù)棧進(jìn)行的。

棧的概念這里就不再贅述,這里知道先進(jìn)后出就好。就像子彈夾。

不同的任務(wù)棧,也稱為不同的啟動模式。

1、任務(wù)棧的分類:

任務(wù)棧有以下四種

  • ** standard 默認(rèn)的啟動模式,標(biāo)準(zhǔn)模式**
  • ** singletop 單一頂部模式 (頂部不會重復(fù))**
  • ** singleTask 單一任務(wù)棧,干掉頭上的其他Activity**
  • ** singleInstance 單一實例(單例),任務(wù)棧里面自已自己一個人**

一般來說用默認(rèn)的就好,當(dāng)我們程序感覺切換奇怪,或者某個activity占用開銷太大之類的,才考慮使用其他的啟動默認(rèn)。

2、指定任務(wù)棧Activity的啟動模式

待會我們再來詳細(xì)解釋這些不同的任務(wù)棧詳細(xì)區(qū)別,現(xiàn)在,我們先看一下怎么指定一個Activity的任務(wù)棧模式,也就是啟動模式。

默認(rèn)的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、從優(yōu)先級來說,代碼指定優(yōu)先于manifest指定
2、兩者各有局限,
manifest無法設(shè)定 FLAG_ACTIVITY_CLEAR_TOP 標(biāo)識
代碼指定無法指定為 singleInstance啟動模式

3、怎么查看當(dāng)前app的任務(wù)棧數(shù)量和任務(wù)棧里面的Activity

3.1 adb shell dumpsys activity

在終端鍵入這樣的指令:
adb shell dumpsys activity

查看當(dāng)前手機(jī)的任務(wù)棧運(yùn)行情況

即可得到我們想要的信息,回車后會列出相當(dāng)多的信息,我們需要找到如下的分類
ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)

下面是一份示例log摘?。?/p>

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),可以看到最近正在操作的那個程序涉及到任務(wù)棧個數(shù),以及每個任務(wù)棧的Activity數(shù)量,根據(jù)位置上下排序,最上面的是棧頂,最下面的是棧底。棧頂就是最近的操作界面

3.3、Running activities (most recent first)

我們把Running activities (most recent first)單獨拿出來說。
下面這份信息,顯示著最近運(yùn)行的一個程序的只有一個任務(wù)棧,任務(wù)棧里面有三個activit,棧頂是ThirdActivity,棧底是FirstActivity。
這個任務(wù)棧的名稱是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}

比如這份終端信息。就表示當(dāng)前當(dāng)前最近的運(yùn)行的有兩個任務(wù)棧的,(為什么一個程序的運(yùn)行有兩個任務(wù)棧?這就涉及到singleTask或者singleInstance啟動模式了,后面會細(xì)說。)
其中,一個任務(wù)棧的是com.taskstack.thirdtask,(我們自己指定的名稱),當(dāng)前這個任務(wù)棧里面只有一個activity。
另外一個任務(wù)棧的名稱是com.amqr.taskstack,這個是系統(tǒng)默認(rèn)自帶的任務(wù)棧,名字就是包名。

我們拿出一條來分析把

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

像這個,528f99b4 #12這么一串里面,這個#12可以說是這個任務(wù)棧的唯一標(biāo)示(有時候會兩個任務(wù)棧出現(xiàn) 任務(wù)棧A=“aaa.bbb.ccc”的aaa.bbb.ccc一樣的情況,想要根本區(qū)分?是否為同一個任務(wù)棧,用的就是這個 #號+num,如果兩個人任務(wù)棧的 #+num 和 A="aaa.bbb.ccc" 兩者都一致的話,那么這兩個任務(wù)棧絕對會同一個任務(wù)棧)
至于最后面的 sz=num,這個num代表這個任務(wù)棧里面當(dāng)前存放了多少個Activity。

什么時候出現(xiàn)兩個任務(wù)棧 aaa..bbb.ccc 相同的但是 #num 不同:singleInstance
什么時候出現(xiàn)兩個任務(wù)棧 aaa.bbb.ccc 和 #num 都相同:singleTask + taskAffinity

3.4、 Recent tasks 手機(jī)當(dāng)前的運(yùn)行的任務(wù)棧

(注意:Recent tasks代表的最近手機(jī)運(yùn)行的程序的任務(wù)棧,不是對應(yīng)正在運(yùn)行的程序的個數(shù)(因為有的程序可能有多個任務(wù)棧),更加不是進(jìn)程數(shù)。)

  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}

這個是記錄手機(jī)當(dāng)前的運(yùn)行的任務(wù)棧數(shù)量的。
當(dāng)我們長按home鍵,或者按下虛擬的菜單鍵,(因機(jī)型而異),反正就是列出正在運(yùn)行的運(yùn)行的任務(wù)棧的界面,假設(shè)我們的正在運(yùn)行的是1個任務(wù)棧,一般來說,這時Rcent tasks就會顯示記錄著3個任務(wù)棧,如果我們手機(jī)顯示正在運(yùn)行兩個2個,那么終端的Rcent tasks就會顯示記錄著4個任務(wù)棧,即為N+2個。(有時候會是N+1,只有com.android.launcher )
多出來的那個兩個上面寫的很清楚,那么就是com.android.launcher 和 com.android.systemui,即為系統(tǒng)啟動器和系統(tǒng)UI界面。

我們在來個圖文詳細(xì)點的吧。

當(dāng)前手機(jī)任務(wù)列表運(yùn)行著3個任務(wù)棧(不要以為任務(wù)棧就是程序,一個程序可能有多個任務(wù)棧)


當(dāng)前手機(jī)任務(wù)列表運(yùn)行著3個任務(wù)棧.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}

.
.
再次說明,列表顯示的是任務(wù)棧,不是程序


一個程序的兩個任務(wù)棧.png

.
.

4、四種啟動模式詳解

4.1、 standard 默認(rèn)的啟動模式,標(biāo)準(zhǔn)模式

結(jié)論:每開啟一個Activity,就會在棧頂添加一個Activity實例。多次間隔或者直接啟動一個甲Activity會添加多個甲的示例,可重復(fù)添加。(間隔 ABA, 直接 ACC或者AAA)
??這里我們需要明白一個事情,Service和ApplicationContext是沒辦法直接開啟一個新的Activity,因為只有Activity類型的Context的Activity才能開啟,但還是有解決辦法的,那就是讓我們要開的那個新的Activity設(shè)置為FLAG_ACTIVITY_NEW_TASK標(biāo)識。

情景實測

比如我們的程序里面有FirstActivity,SecondActivity、ThirdActivity和Fourth四個Activity,為了表示方便,我們就用A,B,C和D來分別指代吧。其中,A為啟動頁。(當(dāng)前A,B,C,D都是standard模式)

  • 情況1: 啟動后顯示A,接著打開B,緊接著打開C。那么顯而易見,這時候只有一個任務(wù)棧,假設(shè)為S1
    啟動A:任務(wù)棧S1里面只有A
    接著打開B:任務(wù)棧里面變成BA,B在A上面,B為棧頂。
    接著打開C,任務(wù)棧里面變成了CBA,棧頂是C,棧底是A。

大概是這個樣子

standard模式簡單ABC手機(jī)界面.gif
standard模式簡單ABC.gif

后面不會這么貼圖了,第一個就圖文說的清楚一些。

  • 情況2: 打開A,打開B,再次打開A
    此時任務(wù)棧: ABA
    那么任務(wù)棧里面的是 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
    任務(wù)棧 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 單一頂部模式 (頂部不會重復(fù))

結(jié)論:如果開啟的Activity已經(jīng)存在一個實例在任務(wù)棧的頂部(僅限于頂部),再去開啟這個Activity,任務(wù)棧不會創(chuàng)建新的Activity的實例了,而是復(fù)用已經(jīng)存在的這個Activity,onNewIntent方法被調(diào)用;之前打開過,但不是位于棧頂,那么還是會產(chǎn)生新的實例入棧,不會回調(diào)onNewIntent方法。

當(dāng)我們把一個Activity設(shè)置為singleTop,當(dāng)我們點擊打開這個Activity的時候,我們打開B頁面,會出現(xiàn)幾種情況:

說明:當(dāng)前A和C都是Standard,B是singleTop

之前沒打開過:
此時任務(wù)棧里面只有A,A所在的任務(wù)棧是S1,這個時候打開singleTop的B,B入棧,入的是S1這個棧,誰打開它進(jìn)入誰的棧,此時S1的情況是BA,B為棧頂。

之前打開過,但是位于棧頂:
那么復(fù)用這個棧,不會有新的實例壓入棧中。同時 onNewIntent 方法會被回調(diào),我們可以利用這個方法獲得頁面?zhèn)鬟^來的消息或者其他操作。

之前打開過,但是不是位于棧頂:
那么還是會產(chǎn)生新的實例入棧。

情景實測

實測前,我們不改動Standard的代碼,只是在manifest里面的將SecondActivity的啟動模式設(shè)置為singleTop

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

以下的談?wù)摱际窃贏和C都是Standard模式,B為singleTop模式的情況進(jìn)行的。

  • 情景一:打開A,然后在打開B
    沒什么好說的,很普通的沒什么區(qū)別

請出終端大哥

    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的作用就發(fā)揮出來了。這是不會產(chǎn)生新的實例,會復(fù)用的頂部的已經(jīng)存在的實例。
    第一次打開B,這時任務(wù)棧里面的順序是BA,B為棧頂
    第二次打開B,這時任務(wù)棧里面的順序還是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
簡單說可以認(rèn)為,由于在我們不開新的實例的基礎(chǔ)上打開自己,所以就只經(jīng)歷了一個 焦點的失去和獲取 的過程。
在SecondActivity中復(fù)寫onNewIntent方法

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

    // onNewIntent 方法,當(dāng)被復(fù)用時調(diào)用
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Log.d("Cur", "SecondActivity onNewIntent");
    }

SecondActivity 的onNewIntent方法被調(diào)用了,代表被復(fù)用。
(需要注意的是,當(dāng)我們第二次打開B的時候,因為沒有新的實例,所以不存在onCreate和onStart方法的執(zhí)行,也不存在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 生命周期明顯發(fā)生變化,只剩下 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 的步驟走完任務(wù)棧里面的順序是CBA,C是棧頂,因為B不處于棧頂所以不會復(fù)用B,此時還是會產(chǎn)生新的B實例進(jìn)行入棧。

有請終端先生:


    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真的還是產(chǎn)生新的實例,沒有復(fù)用,只要B不在頂部,特技就發(fā)揮不出來。

生命周期也是沒有什么特別的

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 單一任務(wù) (整個任務(wù)棧只有一個對應(yīng)自身的實例)

結(jié)論:如果開啟的甲Activity已經(jīng)存在一個實例在任務(wù)棧S1,再去開啟這個Activity,位于棧頂則直接復(fù)用,回調(diào)onNewIntent方法;位于里面,也是復(fù)用,回調(diào)onNewIntent方法,復(fù)用的同時的是直接把自己上方的全部Activity都干掉。

當(dāng)我們把一個Activity設(shè)置為singleTask模式之后,當(dāng)我們點擊開啟這個Activity,會出現(xiàn)3種情況:

說明:打開B,A和C是Standard,B是singleTask

之前沒開啟過:A開啟B的時候,B進(jìn)入A的任務(wù)棧。為了頂部

之前開啟過情況1:如果現(xiàn)在任務(wù)棧情況是BA,B位于棧頂,此時點擊B,那么不會創(chuàng)建新的實例,任務(wù)棧還是BA,回調(diào)onNewIntent方法。

之前開啟過情況2:假如現(xiàn)在任務(wù)棧情況是CBA,C為了棧頂,那么這時打開B,因為B是singleTask,這時不會創(chuàng)建新的實例,但是肯定會把B置為棧頂(B在回到棧頂?shù)臅r候不是跳過去的,而是把自己上面的其他Activity全部干掉,這樣就只剩下自己和自己下面的Activity了),那么這時任務(wù)棧里面的情況就剩下 BA,回調(diào)onNewIntent方法.

.
.

情景實測

好啦,情況說完了,現(xiàn)在應(yīng)該進(jìn)行實測了。
原先的代碼不用改,只需要把SecondActivity指定為singleTask模式,我們這里采用manifes指定

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

以下討論的都是A和C都為Standard,B為singleTask

  • 情況1,打開A,打開B,在打開B。
    最終任務(wù)棧的結(jié)果是 BA. 其中B是棧頂,而且B只有一個,A是棧底。此時onNewIntent方法會被回調(diào)。

這個演示需要有一個圖片,明確清楚地知道我們第二次打開B的時候是打不開,沒反應(yīng)的。

singleTask只有一個實例.gif

通過上圖,我們知道第二次點擊“開啟第2個Activity”是沒有辦法再次打開一個已經(jīng)采用singleTask而且已經(jīng)位于頂部的Activity新的實例。因為他就是那里,不會新增一個新的實例。(可以跟Standard的 情況3 做比較)

終端的數(shù)據(jù)可以證明一切:

 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。
    任務(wù)棧最終的情況 BA
    這個情況有意思,當(dāng)我們第二次打開B的時候,他會把自己上方的全部Activity給干掉,最后只剩下自己和他身下的Activity了。
    說到底也正常,棧的結(jié)構(gòu)看數(shù)據(jù)結(jié)構(gòu)就知道了,其實也可以聯(lián)想到現(xiàn)實,子彈夾我們想把倒數(shù)第二的子彈打出來,自然要把他前面的兩個子彈給清掉。

分兩步來吧看個究竟吧
當(dāng)我們打開A,打開B,打開C,打開D,任務(wù)棧里面的情況是這樣滴

召喚終端大哥

    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}

就在這時,任務(wù)棧的順序是:DCBA ,其中D是棧頂。
這時我們按下打開B,倒數(shù)第二的B 發(fā)射出一道閃閃金光,把上方的C和D全殲了,這時任務(wù)棧的順序就只剩下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,發(fā)現(xiàn)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的談?wù)撝链送戤叀?/p>

.
.

4.4 singleInstance 單一實例(單例),任務(wù)棧里面自已自己一個人

結(jié)論:當(dāng)啟動一個啟動模式為singleInstance的Activity時(之前沒啟動過),這時系統(tǒng)將開辟出另外一個任務(wù)棧,用于存放這個Activity,而且這個新的任務(wù)棧只能存放自身這唯一一個Activity。singleInstance頁面作為前臺任務(wù)打開自己打開自己,則復(fù)用,任務(wù)棧順序無變化;singleInstance頁面作為后臺任務(wù)棧,則切換成為前臺任務(wù)棧,無新實例產(chǎn)生,復(fù)用。

復(fù)用就會調(diào)用onNewIntent方法。

情景實測

以下的A和B都是Standard,然后C和D我們都通manifest把啟動模式設(shè)定為 singleInstance

        <activity android:name=".ThirdActivity"

            android:launchMode="singleInstance"
            />
        <activity android:name=".FourtActivity"
            android:launchMode="singleInstance"
            />
  • 情景1:打開A,打開B,打開為singleInstance的C
    打開A,A存在于任務(wù)棧S1,此時任務(wù)棧里面只有A,
    打開B, B入棧,進(jìn)入S1,此時S1的里面有兩個Activity,順序為BA,B為棧頂。

打開C,因為C是singleInstance,所以自己的任務(wù)棧。很明顯,這個時候C不會進(jìn)入S1的。而是會開辟出來一個全新的任務(wù)棧,我們就稱為任務(wù)棧S2吧,
注意,此時S2屬于前臺任務(wù)棧,S1屬于后臺任務(wù)棧。

如果非要排個需要序號的話,那么就是 (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就不同就代表是兩個不同任務(wù)棧。#號帶的數(shù)字是最能區(qū)分的標(biāo)志。我們說的 # 是緊挨著任務(wù)棧名稱左邊的那個#。

生命周期沒什么影響

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的條件下再按下返回鍵。

我們再續(xù)前緣接著說。
第一次按下返回鍵,S2銷毀,S1成為前臺任務(wù)棧,S1任務(wù)棧里面的順序是BA,其中B是棧頂。
界面停留在B界面。

第二次按下返回鍵,界面退到A界面,S1任務(wù)里面B出棧,任務(wù)棧里面只剩A。

第三次按下返回鍵,整個程序退出,任務(wù)棧S1銷毀。

  • 情景3,打開A,打開B,打開singleInstance的C,C打開C(復(fù)用,無新實例),接著打開singleInstance的D,接著D打開C。(復(fù)用,無新實例,改變?nèi)蝿?wù)棧順序)
    C和D都復(fù)寫onNewIntent方法以便觀察。

那么這時會產(chǎn)生3個任務(wù)棧,A和B所在的為S1,C所在的任務(wù)棧為S2,D所所在的任務(wù)棧為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,復(fù)用,終端信息一致。就不附上了。
生命周期有所不同。

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

發(fā)現(xiàn)多了一個任務(wù)棧。

步驟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

沒有新的任務(wù)棧產(chǎn)生,前臺任務(wù)棧發(fā)了變化。任務(wù)棧里面也不會有新的Activity產(chǎn)生。

singleInstance分析至此結(jié)束。

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

說在前面

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

按照官網(wǎng)大概是這么說taskAffinity 和 allowTaskReparenting的:
taskAffinity: 可以指定一個Activity放入哪一個任務(wù)棧中,利用taskAffinity制定一個任務(wù)棧的名稱,把Activity放進(jìn)這個任務(wù)棧中。實現(xiàn)這個過程需要singleTask和allowTaskReparenting兩者的協(xié)助。

allowTaskReparenting:參數(shù)是boolean,如果我們利用taskAffinity讓Activity放入一個指定的任務(wù)棧,需要allowTaskReparenting的同意,為true就可以跟著別人跑,為false就乖乖在原地呆著那都不許去。

按照的官網(wǎng)的說法,實測后發(fā)現(xiàn),taskAffinity確實可以讓Activity跑到指定的任務(wù)棧(不用跟app自帶的任務(wù)?;炝耍莂llowTaskReparenting沒什么作用,設(shè)置或者不設(shè)置沒什么改變。設(shè)置為true,設(shè)置為false,或者不設(shè)置,一點都沒區(qū)別。

即是說官方說,taskAffinity要和allowTaskReparenting配合著使用,實測是上不用,taskAffinity單兵作戰(zhàn)也是可以的。(個人看法,疏忽之處請熟悉的朋友指點一下)

下面開始正式分析。

5.1、taskAffinity

taskAffinity屬性用于給Activity單獨指定任務(wù)棧名稱。這個名稱不能和包名相同,否則就沒有意義。注意taskAffinity屬性值為String,而且中間必須包含有分隔符 . (英文狀態(tài)下的點),比如com.baidu.test

另外,如果想要指定一個非包名的任務(wù)棧,該Activity一定要把啟動模式設(shè)置為singleTask模式,否則不會生效。如果taskAffinity指定的名稱是其他程序的包名,那么可以不結(jié)合singleTask。念起來好像有點拗口,看下面的實測就知道怎么回事了。

注意:任務(wù)棧分為前臺任務(wù)棧和后臺任務(wù)棧,后臺任務(wù)棧里面的Activity全部處于onStop狀態(tài)。

在minifest里面,application可以設(shè)定taskAffinity,activity也可以設(shè)定taskAffinity。
taskAffinity設(shè)定的任務(wù)棧我們也稱其為一個宿主任務(wù)棧。

  • application設(shè)定

    • applicatipn如果不設(shè)定,那么就系統(tǒng)默認(rèn)設(shè)定為包名。如果設(shè)定了,activity跟著application走,application指定的是什么activity的任務(wù)棧的名稱就是什么。(application自帶的不設(shè)定,一般我們也不手動設(shè)定,要設(shè)定也是單獨在activity里面設(shè)定)
  • activity設(shè)定

    • 設(shè)定taskAffinity之后,當(dāng)啟動這個Activity之后,如果之前沒有任務(wù)棧的存在,那么就啟動系統(tǒng)會開辟出來一個新的任務(wù)棧(或者叫宿主任務(wù)棧),用于存放這個activity,如果已經(jīng)存在了這個任務(wù)棧,那么這個activity就對應(yīng)進(jìn)入已經(jīng)的宿主任務(wù)棧。(設(shè)定taskAffinity,不管之前存不存在,反正就不跟默認(rèn)的混了,自己只認(rèn)指定的任務(wù)棧,當(dāng)然,如果你非要把taskAffinity指定自己的包名那也沒辦法,只是那沒撒意思嘛)

我們利用taskAffinity+taskAffinity指定非手機(jī)里面任何程序的包名的任務(wù)棧時,這個任務(wù)是可以容納多個activity的。比如現(xiàn)在有A,B,C三個界面,B和C都啟動模式都是taskAffinity,而且taskAffinity指定的都是同一個包名。
那么當(dāng)我們A開啟B,B再開啟C的時候,結(jié)果就是A在app默認(rèn)自帶的任務(wù)棧S1里,而B和C在同一個任務(wù)棧S2里面。這是S2里面的順序是CB,C是棧頂。然后這個時候從C打開B,那么事情來了,本來的C下面的B會再次匯聚天地能量,發(fā)出一道閃閃金光,把自己上方的所有Activity全部殲滅,這個時候C就掛了??梢?,不管和誰合作,singleTask依舊霸道,不可阻擋,誰擋誰卒。

有代碼有真相,先來看一下如何利用taskAffinity給activty指定任務(wù)棧:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.amqr.newtaskone" >

    <activity android:name=".OtherActivity"
// android:taskAffinity 的使用一般都是有singeTask一起出現(xiàn)的
        android:taskAffinity="com.task.try"

        />

如上,我們的包名是 com.amqr.newtaskone,然后我們給我們的activity指定了宿主任務(wù)棧的的名稱為 com.task.try 。這樣他就和系統(tǒng)默認(rèn)的任務(wù)棧名稱不同了。

5.2、allowTaskReparenting

allowTaskReparenting:參數(shù)是boolean。
如果我們利用taskAffinity讓Activity放入一個指定的任務(wù)棧,需要allowTaskReparenting的同意,為true就可以跟著別人跑,為false就乖乖在原地呆著除了自己家那都不許去。

怎么用

        <activity android:name=".OtherActivity"

            android:taskAffinity="com.task.try"
            android:allowTaskReparenting="true"

            />

5.3 實測

情況1和情況2,app2的activity指定的任務(wù)棧名稱是app1的包名,所以不需要singleTask(這里指定包名是為了演示效果)

  • 情況1:新建兩個app。
    app1什么都不改,自帶一個MainActivity(布局文件標(biāo)識一下是app1的Mainactivity)。
    在app2的manifest給app2的MainAcivity添加android:taskAffinity屬性,指定包名為 app1的包名。

也就是說,當(dāng)app2的MainActivity的啟動的時候會把a(bǔ)pp的默認(rèn)任務(wù)棧當(dāng)做自己的宿主。

我們把兩個程序安裝一下,清掉其他正在運(yùn)行的程序
運(yùn)行app1,然后按下home鍵(讓app1變成后臺程序),接著打開app2,我們會發(fā)現(xiàn),這時候進(jìn)入的不是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

終端大哥也顯示為只有一個任務(wù)棧,那就是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,結(jié)果打開是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

我們發(fā)現(xiàn),我們點擊啟動的app2,結(jié)果啟動的卻是app1,而且,在運(yùn)行列表里面看不到app2的存在,只有一個app1。

里面發(fā)生的是這樣的一個過程,
當(dāng)我們啟動app1時:app1的任務(wù)棧com.amqr.taskaffinity1 正常作為一個任務(wù)棧進(jìn)入系統(tǒng),
按下home鍵時:任務(wù)棧 com.amqr.taskaffinity1 成為了一個后臺任務(wù)棧
點擊app2時: 本來應(yīng)該打開的是app2的界面,但是要打開的MainActivity2發(fā)現(xiàn)自身含有android:taskAffinity,而指定的宿主任務(wù)棧就是app1打開后就存在的任務(wù)棧 com.amqr.taskaffinity1 ,那么這時候 com.amqr.taskaffinity1 就成為了 MainActivity2 的宿主任務(wù)棧。
按道理,這個時候本來應(yīng)該是 MainActivity2 入棧而且作為棧頂?shù)陌?,但是事實卻沒有這么發(fā)生。也就是說,這個時候,MainActivity2沒有入棧,對的,沒有入棧。他是跑過來搞笑的,通知了一下別人可以變成前臺任務(wù)棧了,然后自己沒有入棧,具體詳情,不得而知。
實際上發(fā)生的事情是:com.amqr.taskaffinity1 由后臺任務(wù)棧轉(zhuǎn)為前臺任務(wù)棧,而那個MainActity沒有入棧,從終端的信息就可以看得出來。

有人說,你沒按谷歌的來,沒有把a(bǔ)llowTaskReparenting設(shè)置為true,好,那我們進(jìn)行其概況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>

我們重新安裝兩個程序,接著來一遍。
(清除所有運(yùn)行的程序,打開app1,按下home,打開app2)

宿主情況2.gif

結(jié)果發(fā)現(xiàn)一樣,還是貼一下對應(yīng)的終端信息吧

    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}

一個樣,其實就算設(shè)置為false也是沒有變化,有興趣的可以自己試一下。

注意:宿主不是絕對化的,當(dāng)兩個app都認(rèn)定一個宿主后,?就先來后到了,誰先開啟那個任務(wù)棧誰是老大。比如說,我們在可以清掉所有運(yùn)行的程序,先開啟app2,然后再開啟app1,我們會發(fā)現(xiàn)打開的是app2,就不是app1了。

app2宿主.gif
  • 情況三:
    情況三不是指定程序的包名作為指定的任務(wù)棧名稱,所以需要啟動的模式singleTask一起工作。(如果不跟singleTask合作,又不指定為程序的包名,那么設(shè)置android:taskAffinity不生效)

情況三我們這樣弄,app1和app2各自都有兩個頁面,各自的啟動頁都是普通常見的,然后各自的都有另外一個其他頁面,這分屬于兩個app的其他頁面我們都給他們指定同一個任務(wù)棧名(這個任務(wù)站名不跟任意一個app包名相同)

也就是說,app1里面有MainActivity1和App1Other兩個Activity,
app2里面有MainActivity2和App2Other兩個Activity

MainActivity1和MainActivity2都是普通的Standard
而App1Other和App2Other都在清單文件指定了自定義的任務(wù)棧名稱和singelTask啟動模式。詳見下方代碼

我們這樣操作:
第一階段:先打開app1,再打開App1Other頁面,接著按下home鍵
第二階段:(接按下home件之后)打開app2,打開app2的App2Other頁面

先貼上相關(guān)圖片和代碼先:

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鍵盤,查看運(yùn)行列表,會看見變成了兩個任務(wù)棧(任務(wù)棧是兩個,程序還是一個,進(jìn)程更加只是一個)


taskAffinity1.gif

任務(wù)棧信息



    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


從上面的圖文我們知道,
當(dāng)我們點擊app1時,默認(rèn)的用的是系統(tǒng)的根據(jù)包名定的默認(rèn)任務(wù)棧,這個沒什么可說的。

但是當(dāng)我們點擊打開 app1Other 頁面時,就多出來一個任務(wù)棧,棧名是我們指定的com.amqr.independent。

而且觀察運(yùn)行程序的列表,發(fā)現(xiàn)多了個圖標(biāo),但是要知道,這個絕對不是多了一個進(jìn)程,只是我們每多產(chǎn)生多一個任務(wù)棧,任務(wù)列表就會多出來一個圖標(biāo)。

一個程序的兩個任務(wù)棧.png

進(jìn)程不變

Paste_Image.png
  • 第二階段

緊接上面的環(huán)節(jié)
接下來我們來打開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

我們發(fā)現(xiàn)了,
圖片的任務(wù)列表是3個,終端的日志任務(wù)??雌饋硐癯霈F(xiàn)了4個,其實是3個任務(wù)棧,因為有兩個是重復(fù)的,為什么重復(fù)了呢?

我們先來看一下這里面的兩句:
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可以知道這肯定是同一個任務(wù)棧
其實這個sz后面表示的就是activity的個數(shù)(已測)
至于為什么分開顯示,這個應(yīng)該是終端顯示的一種表示方法,終端的具體規(guī)則不太清楚。這里不是我們深究的方向。到了這里明白運(yùn)行的是3個任務(wù)棧就好了。

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

  • 情況4

app1里面弄3個activity,MainActivity,App1Other,App3Activity,分別用A,B,C指代,A是Standard的啟動頁,B和C都是single + taskAffinity且指定的任務(wù)棧名稱相同(不跟任何程序包名相同)。

操作過程,
第一階段:運(yùn)行app1,啟動頁A啟動B,

第二階段:
再通過B啟動C
最后我們發(fā)現(xiàn),在singleTask + taskAffinity模式下還是誰當(dāng)滅滅誰,在它前面的全部遭殃。

貼上代碼和相關(guān)圖片:

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,這時候我們的指定任務(wù)棧里面的順序就是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}

第一階段結(jié)束。

.
.
第二階段:

緊接著上面的操作,在上面的操作里面我們的指定的任務(wù)棧里面已經(jīng)有了兩個都是singleTask的任務(wù)在里面了,B在C的下面,C是棧頂,這個時候我們從C開啟B,結(jié)果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}

本篇就先說道這里吧,下次說標(biāo)志位—— FLAG.

對5.2情況3有興趣的可以參考這里Android-Activity5.3情景3之完整Log參考
完。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容