本文出自 “阿敏其人” 簡書博客,轉載或引用請注明出處。
Android-Activity所應該了解的大概就這樣。(上)
Android-Activity所應該了解的大概就這樣。(下)
五、任務棧/Activity的啟動模式
知道生命周期、線程的優(yōu)先級和Activity的異常銷毀,下面我們來認識一下任務棧。
我們上面進行的那么多描述和代碼,都是在standard這種默認的任務棧進行的。
棧的概念這里就不再贅述,這里知道先進后出就好。就像子彈夾。
不同的任務棧,也稱為不同的啟動模式。
1、任務棧的分類:
任務棧有以下四種
- ** standard 默認的啟動模式,標準模式**
- ** singletop 單一頂部模式 (頂部不會重復)**
- ** singleTask 單一任務棧,干掉頭上的其他Activity**
- ** singleInstance 單一實例(單例),任務棧里面自已自己一個人**
一般來說用默認的就好,當我們程序感覺切換奇怪,或者某個activity占用開銷太大之類的,才考慮使用其他的啟動默認。
2、指定任務棧Activity的啟動模式
待會我們再來詳細解釋這些不同的任務棧詳細區(qū)別,現在,我們先看一下怎么指定一個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、從優(yōu)先級來說,代碼指定優(yōu)先于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,這個是系統(tǒng)默認自帶的任務棧,名字就是包名。
我們拿出一條來分析把
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一樣的情況,想要根本區(qū)分?是否為同一個任務棧,用的就是這個 #號+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,即為系統(tǒng)啟動器和系統(tǒng)UI界面。
我們在來個圖文詳細點的吧。
當前手機任務列表運行著3個任務棧(不要以為任務棧就是程序,一個程序可能有多個任務棧)
查看一下終端,會顯示記錄著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}
.
.
再次說明,列表顯示的是任務棧,不是程序
.
.
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。
大概是這個樣子
后面不會這么貼圖了,第一個就圖文說的清楚一些。
- 情況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,任務棧不會創(chuàng)建新的Activity的實例了,而是復用已經存在的這個Activity,onNewIntent方法被調用;之前打開過,但不是位于棧頂,那么還是會產生新的實例入棧,不會回調onNewIntent方法。
當我們把一個Activity設置為singleTop,當我們點擊打開這個Activity的時候,我們打開B頁面,會出現幾種情況:
說明:當前A和C都是Standard,B是singleTop
之前沒打開過:
此時任務棧里面只有A,A所在的任務棧是S1,這個時候打開singleTop的B,B入棧,入的是S1這個棧,誰打開它進入誰的棧,此時S1的情況是BA,B為棧頂。
之前打開過,但是位于棧頂:
那么復用這個棧,不會有新的實例壓入棧中。同時 onNewIntent 方法會被回調,我們可以利用這個方法獲得頁面?zhèn)鬟^來的消息或者其他操作。
之前打開過,但是不是位于棧頂:
那么還是會產生新的實例入棧。
情景實測
實測前,我們不改動Standard的代碼,只是在manifest里面的將SecondActivity的啟動模式設置為singleTop
<activity android:name=".SecondActivity"
android:launchMode="singleTop" />
以下的談論都是在A和C都是Standard模式,B為singleTop模式的情況進行的。
- 情景一:打開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ā)揮出來了。這是不會產生新的實例,會復用的頂部的已經存在的實例。
第一次打開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方法的執(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 的步驟走完任務棧里面的順序是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不在頂部,特技就發(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 單一任務 (整個任務棧只有一個對應自身的實例)
結論:如果開啟的甲Activity已經存在一個實例在任務棧S1,再去開啟這個Activity,位于棧頂則直接復用,回調onNewIntent方法;位于里面,也是復用,回調onNewIntent方法,復用的同時的是直接把自己上方的全部Activity都干掉。
當我們把一個Activity設置為singleTask模式之后,當我們點擊開啟這個Activity,會出現3種情況:
說明:打開B,A和C是Standard,B是singleTask
之前沒開啟過:A開啟B的時候,B進入A的任務棧。為了頂部
之前開啟過情況1:如果現在任務棧情況是BA,B位于棧頂,此時點擊B,那么不會創(chuàng)建新的實例,任務棧還是BA,回調onNewIntent方法。
之前開啟過情況2:假如現在任務棧情況是CBA,C為了棧頂,那么這時打開B,因為B是singleTask,這時不會創(chuàng)建新的實例,但是肯定會把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的時候是打不開,沒反應的。
通過上圖,我們知道第二次點擊“開啟第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了。
說到底也正常,棧的結構看數據結構就知道了,其實也可以聯(lián)想到現實,子彈夾我們想把倒數第二的子彈打出來,自然要把他前面的兩個子彈給清掉。
分兩步來吧看個究竟吧
當我們打開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 發(fā)射出一道閃閃金光,把上方的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,發(fā)現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時(之前沒啟動過),這時系統(tǒng)將開辟出另外一個任務棧,用于存放這個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就不同就代表是兩個不同任務棧。#號帶的數字是最能區(qū)分的標志。我們說的 # 是緊挨著任務棧名稱左邊的那個#。
生命周期沒什么影響
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成為前臺任務棧,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
發(fā)現多了一個任務棧。
步驟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
沒有新的任務棧產生,前臺任務棧發(fā)了變化。任務棧里面也不會有新的Activity產生。
singleInstance分析至此結束。
5、另說singleTask的小伙伴:taskAffinity + allowTaskReparenting
說在前面
taskAffinity是singleTask的好伙伴,這是肯定的。
按照官網大概是這么說taskAffinity 和 allowTaskReparenting的:
taskAffinity: 可以指定一個Activity放入哪一個任務棧中,利用taskAffinity制定一個任務棧的名稱,把Activity放進這個任務棧中。實現這個過程需要singleTask和allowTaskReparenting兩者的協(xié)助。
allowTaskReparenting:參數是boolean,如果我們利用taskAffinity讓Activity放入一個指定的任務棧,需要allowTaskReparenting的同意,為true就可以跟著別人跑,為false就乖乖在原地呆著那都不許去。
按照的官網的說法,實測后發(fā)現,taskAffinity確實可以讓Activity跑到指定的任務棧(不用跟app自帶的任務棧混了),但是allowTaskReparenting沒什么作用,設置或者不設置沒什么改變。設置為true,設置為false,或者不設置,一點都沒區(qū)別。
即是說官方說,taskAffinity要和allowTaskReparenting配合著使用,實測是上不用,taskAffinity單兵作戰(zhàn)也是可以的。(個人看法,疏忽之處請熟悉的朋友指點一下)
下面開始正式分析。
5.1、taskAffinity
taskAffinity屬性用于給Activity單獨指定任務棧名稱。這個名稱不能和包名相同,否則就沒有意義。注意taskAffinity屬性值為String,而且中間必須包含有分隔符 . (英文狀態(tài)下的點),比如com.baidu.test
另外,如果想要指定一個非包名的任務棧,該Activity一定要把啟動模式設置為singleTask模式,否則不會生效。如果taskAffinity指定的名稱是其他程序的包名,那么可以不結合singleTask。念起來好像有點拗口,看下面的實測就知道怎么回事了。
注意:任務棧分為前臺任務棧和后臺任務棧,后臺任務棧里面的Activity全部處于onStop狀態(tài)。
在minifest里面,application可以設定taskAffinity,activity也可以設定taskAffinity。
taskAffinity設定的任務棧我們也稱其為一個宿主任務棧。
-
application設定
- applicatipn如果不設定,那么就系統(tǒng)默認設定為包名。如果設定了,activity跟著application走,application指定的是什么activity的任務棧的名稱就是什么。(application自帶的不設定,一般我們也不手動設定,要設定也是單獨在activity里面設定)
-
activity設定
- 設定taskAffinity之后,當啟動這個Activity之后,如果之前沒有任務棧的存在,那么就啟動系統(tǒng)會開辟出來一個新的任務棧(或者叫宿主任務棧),用于存放這個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會再次匯聚天地能量,發(fā)出一道閃閃金光,把自己上方的所有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 。這樣他就和系統(tǒng)默認的任務棧名稱不同了。
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,我們會發(fā)現,這時候進入的不是app2的界面,而是app1的。
先來看一下兩個app的Mainactivty本來的面目:
app1的MainActivityUI截圖
app2的MainActivityUI截圖
接下來看一下各自的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>
查看效果:
終端大哥也顯示為只有一個任務棧,那就是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
我們發(fā)現,我們點擊啟動的app2,結果啟動的卻是app1,而且,在運行列表里面看不到app2的存在,只有一個app1。
里面發(fā)生的是這樣的一個過程,
當我們啟動app1時:app1的任務棧com.amqr.taskaffinity1 正常作為一個任務棧進入系統(tǒng),
按下home鍵時:任務棧 com.amqr.taskaffinity1 成為了一個后臺任務棧
點擊app2時: 本來應該打開的是app2的界面,但是要打開的MainActivity2發(fā)現自身含有android:taskAffinity,而指定的宿主任務棧就是app1打開后就存在的任務棧 com.amqr.taskaffinity1 ,那么這時候 com.amqr.taskaffinity1 就成為了 MainActivity2 的宿主任務棧。
按道理,這個時候本來應該是 MainActivity2 入棧而且作為棧頂的啊,但是事實卻沒有這么發(fā)生。也就是說,這個時候,MainActivity2沒有入棧,對的,沒有入棧。他是跑過來搞笑的,通知了一下別人可以變成前臺任務棧了,然后自己沒有入棧,具體詳情,不得而知。
實際上發(fā)生的事情是: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)
結果發(fā)現一樣,還是貼一下對應的終端信息吧
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,我們會發(fā)現打開的是app2,就不是app1了。
- 情況三:
情況三不是指定程序的包名作為指定的任務棧名稱,所以需要啟動的模式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頁面
先貼上相關圖片和代碼先:
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鍵盤,查看運行列表,會看見變成了兩個任務棧(任務棧是兩個,程序還是一個,進程更加只是一個)
任務棧信息
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時,默認的用的是系統(tǒng)的根據包名定的默認任務棧,這個沒什么可說的。
但是當我們點擊打開 app1Other 頁面時,就多出來一個任務棧,棧名是我們指定的com.amqr.independent。
而且觀察運行程序的列表,發(fā)現多了個圖標,但是要知道,這個絕對不是多了一個進程,只是我們每多產生多一個任務棧,任務列表就會多出來一個圖標。
進程不變
- 第二階段
緊接上面的環(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}
程序截圖
我們發(fā)現了,
圖片的任務列表是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的個數(已測)
至于為什么分開顯示,這個應該是終端顯示的一種表示方法,終端的具體規(guī)則不太清楚。這里不是我們深究的方向。到了這里明白運行的是3個任務棧就好了。
我們這里了明白singleTask + taskAffinity是如何使用的就好了。
- 情況4
app1里面弄3個activity,MainActivity,App1Other,App3Activity,分別用A,B,C指代,A是Standard的啟動頁,B和C都是single + taskAffinity且指定的任務棧名稱相同(不跟任何程序包名相同)。
操作過程,
第一階段:運行app1,啟動頁A啟動B,
第二階段:
再通過B啟動C
最后我們發(fā)現,在singleTask + taskAffinity模式下還是誰當滅滅誰,在它前面的全部遭殃。
貼上代碼和相關圖片:
manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.amqr.taskaffinity1" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
>
<activity android:name=".MainActivity1">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".App1Other"
android:taskAffinity="com.amqr.independent"
android:launchMode="singleTask"
/>
<activity android:name=".App3Activity"
android:taskAffinity="com.amqr.independent"
android:launchMode="singleTask"
/>
</application>
</manifest>
第一階段:
A開啟B,B開啟C,這時候我們的指定任務棧里面的順序就是CB
第一階段終端信息
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卒,掛了。
第二階段終端信息:
Running activities (most recent first):
TaskRecord{5295e6f8 #1 A=com.android.launcher U=0 sz=1}
Run #1: ActivityRecord{5295cb64 u0 com.android.launcher/com.android.launcher2.Launcher t1}
TaskRecord{52abf0cc #117 A=com.android.systemui U=0 sz=1}
Run #0: ActivityRecord{5290923c u0 com.android.systemui/.recent.RecentsActivity t117}
本篇就先說道這里吧,下次說標志位—— FLAG.
對5.2情況3有興趣的可以參考這里Android-Activity5.3情景3之完整Log參考
完。