本文出自 “阿敏其人” 簡書博客春感,轉(zhuǎn)載或引用請注明出處慎冤。
Android-Activity所應該了解的大概就這樣疼燥。(上)
Android-Activity所應該了解的大概就這樣。(下)
五蚁堤、任務棧/Activity的啟動模式
知道生命周期醉者、線程的優(yōu)先級和Activity的異常銷毀,下面我們來認識一下任務棧违寿。
我們上面進行的那么多描述和代碼湃交,都是在standard這種默認的任務棧進行的。
棧的概念這里就不再贅述藤巢,這里知道先進后出就好搞莺。就像子彈夾。
不同的任務棧掂咒,也稱為不同的啟動模式才沧。
1、任務棧的分類:
任務棧有以下四種
- ** standard 默認的啟動模式,標準模式**
- ** singletop 單一頂部模式 (頂部不會重復)**
- ** singleTask 單一任務棧绍刮,干掉頭上的其他Activity**
- ** singleInstance 單一實例(單例)温圆,任務棧里面自已自己一個人**
一般來說用默認的就好,當我們程序感覺切換奇怪孩革,或者某個activity占用開銷太大之類的岁歉,才考慮使用其他的啟動默認。
2膝蜈、指定任務棧Activity的啟動模式
待會我們再來詳細解釋這些不同的任務棧詳細區(qū)別锅移,現(xiàn)在,我們先看一下怎么指定一個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的任務棧數(shù)量和任務棧里面的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),可以看到最近正在操作的那個程序涉及到任務棧個數(shù)忿等,以及每個任務棧的Activity數(shù)量栖忠,根據(jù)位置上下排序,最上面的是棧頂贸街,最下面的是棧底庵寞。棧頂就是最近的操作界面
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可以說是這個任務棧的唯一標示(有時候會兩個任務棧出現(xiàn) 任務棧A=“aaa.bbb.ccc”的aaa.bbb.ccc一樣的情況,想要根本區(qū)分?是否為同一個任務棧杂穷,用的就是這個 #號+num悍缠,如果兩個人任務棧的 #+num 和 A="aaa.bbb.ccc" 兩者都一致的話,那么這兩個任務棧絕對會同一個任務棧)
至于最后面的 sz=num耐量,這個num代表這個任務棧里面當前存放了多少個Activity飞蚓。
什么時候出現(xiàn)兩個任務棧 aaa..bbb.ccc 相同的但是 #num 不同:singleInstance
什么時候出現(xiàn)兩個任務棧 aaa.bbb.ccc 和 #num 都相同:singleTask + taskAffinity
3.4、 Recent tasks 手機當前的運行的任務棧
(注意:Recent tasks代表的最近手機運行的程序的任務棧廊蜒,不是對應正在運行的程序的個數(shù)(因為有的程序可能有多個任務棧)趴拧,更加不是進程數(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}
這個是記錄手機當前的運行的任務棧數(shù)量的。
當我們長按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已經(jīng)存在一個實例在任務棧的頂部(僅限于頂部),再去開啟這個Activity芭挽,任務棧不會創(chuàng)建新的Activity的實例了,而是復用已經(jīng)存在的這個Activity草描,onNewIntent方法被調(diào)用览绿;之前打開過,但不是位于棧頂穗慕,那么還是會產(chǎn)生新的實例入棧饿敲,不會回調(diào)onNewIntent方法。
當我們把一個Activity設置為singleTop逛绵,當我們點擊打開這個Activity的時候怀各,我們打開B頁面倔韭,會出現(xiàn)幾種情況:
說明:當前A和C都是Standard,B是singleTop
之前沒打開過:
此時任務棧里面只有A瓢对,A所在的任務棧是S1寿酌,這個時候打開singleTop的B,B入棧硕蛹,入的是S1這個棧醇疼,誰打開它進入誰的棧,此時S1的情況是BA法焰,B為棧頂秧荆。
之前打開過,但是位于棧頂:
那么復用這個棧埃仪,不會有新的實例壓入棧中乙濒。同時 onNewIntent 方法會被回調(diào),我們可以利用這個方法獲得頁面?zhèn)鬟^來的消息或者其他操作卵蛉。
之前打開過颁股,但是不是位于棧頂:
那么還是會產(chǎ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ā)揮出來了。這是不會產(chǎn)生新的實例胁澳,會復用的頂部的已經(jīng)存在的實例该互。
第一次打開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
簡單說可以認為随橘,由于在我們不開新的實例的基礎上打開自己,所以就只經(jīng)歷了一個 焦點的失去和獲取 的過程锦庸。
在SecondActivity中復寫onNewIntent方法
@Override
protected void onDestroy() {
super.onDestroy();
Log.d("Cur", "SecondActivity onDestroy");
}
// onNewIntent 方法机蔗,當被復用時調(diào)用
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d("Cur", "SecondActivity onNewIntent");
}
SecondActivity 的onNewIntent方法被調(diào)用了,代表被復用。
(需要注意的是萝嘁,當我們第二次打開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财岔,此時還是會產(chǎn)生新的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真的還是產(chǎn)生新的實例匠璧,沒有復用桐款,只要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已經(jīng)存在一個實例在任務棧S1酿雪,再去開啟這個Activity遏暴,位于棧頂則直接復用,回調(diào)onNewIntent方法指黎;位于里面朋凉,也是復用,回調(diào)onNewIntent方法醋安,復用的同時的是直接把自己上方的全部Activity都干掉杂彭。
當我們把一個Activity設置為singleTask模式之后,當我們點擊開啟這個Activity吓揪,會出現(xiàn)3種情況:
說明:打開B亲怠,A和C是Standard,B是singleTask
之前沒開啟過:A開啟B的時候柠辞,B進入A的任務棧团秽。為了頂部
之前開啟過情況1:如果現(xiàn)在任務棧情況是BA,B位于棧頂,此時點擊B徙垫,那么不會創(chuàng)建新的實例讥裤,任務棧還是BA,回調(diào)onNewIntent方法姻报。
之前開啟過情況2:假如現(xiàn)在任務棧情況是CBA己英,C為了棧頂,那么這時打開B吴旋,因為B是singleTask损肛,這時不會創(chuàng)建新的實例,但是肯定會把B置為棧頂(B在回到棧頂?shù)臅r候不是跳過去的荣瑟,而是把自己上面的其他Activity全部干掉治拿,這樣就只剩下自己和自己下面的Activity了),那么這時任務棧里面的情況就剩下 BA笆焰,回調(diào)onNewIntent方法.
.
.
情景實測
好啦劫谅,情況說完了,現(xiàn)在應該進行實測了嚷掠。
原先的代碼不用改捏检,只需要把SecondActivity指定為singleTask模式,我們這里采用manifes指定
<activity android:name=".SecondActivity"
android:launchMode="singleTask"
/>
以下討論的都是A和C都為Standard不皆,B為singleTask
- 情況1贯城,打開A,打開B霹娄,在打開B能犯。
最終任務棧的結果是 BA. 其中B是棧頂,而且B只有一個犬耻,A是棧底踩晶。此時onNewIntent方法會被回調(diào)。
這個演示需要有一個圖片香追,明確清楚地知道我們第二次打開B的時候是打不開合瓢,沒反應的。
通過上圖透典,我們知道第二次點擊“開啟第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遗增。
任務棧最終的情況 BA
這個情況有意思,當我們第二次打開B的時候款青,他會把自己上方的全部Activity給干掉做修,最后只剩下自己和他身下的Activity了。
說到底也正常抡草,棧的結構看數(shù)據(jù)結構就知道了饰及,其實也可以聯(lián)想到現(xiàn)實,子彈夾我們想把倒數(shù)第二的子彈打出來康震,自然要把他前面的兩個子彈給清掉燎含。
分兩步來吧看個究竟吧
當我們打開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,倒數(shù)第二的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ā)現(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的談論至此完畢尚辑。
.
.
4.4 singleInstance 單一實例(單例)辑鲤,任務棧里面自已自己一個人
結論:當啟動一個啟動模式為singleInstance的Activity時(之前沒啟動過),這時系統(tǒng)將開辟出另外一個任務棧杠茬,用于存放這個Activity月褥,而且這個新的任務棧只能存放自身這唯一一個Activity。singleInstance頁面作為前臺任務打開自己打開自己瓢喉,則復用宁赤,任務棧順序無變化;singleInstance頁面作為后臺任務棧栓票,則切換成為前臺任務棧决左,無新實例產(chǎn)生,復用。
復用就會調(diào)用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就不同就代表是兩個不同任務棧糯崎。#號帶的數(shù)字是最能區(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瑞妇。(復用,無新實例梭冠,改變?nèi)蝿諚m樞颍?br> C和D都復寫onNewIntent方法以便觀察。
那么這時會產(chǎn)生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ā)現(xiàn)多了一個任務棧滞诺。
步驟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
沒有新的任務棧產(chǎn)生形导,前臺任務棧發(fā)了變化。任務棧里面也不會有新的Activity產(chǎn)生习霹。
singleInstance分析至此結束朵耕。
5、另說singleTask的小伙伴:taskAffinity + allowTaskReparenting
說在前面
taskAffinity是singleTask的好伙伴淋叶,這是肯定的阎曹。
按照官網(wǎng)大概是這么說taskAffinity 和 allowTaskReparenting的:
taskAffinity: 可以指定一個Activity放入哪一個任務棧中,利用taskAffinity制定一個任務棧的名稱煞檩,把Activity放進這個任務棧中处嫌。實現(xiàn)這個過程需要singleTask和allowTaskReparenting兩者的協(xié)助。
allowTaskReparenting:參數(shù)是boolean斟湃,如果我們利用taskAffinity讓Activity放入一個指定的任務棧熏迹,需要allowTaskReparenting的同意,為true就可以跟著別人跑桐早,為false就乖乖在原地呆著那都不許去癣缅。
按照的官網(wǎng)的說法,實測后發(fā)現(xiàn)哄酝,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装悲,如果已經(jīng)存在了這個任務棧昏鹃,那么這個activity就對應進入已經(jīng)的宿主任務棧。(設定taskAffinity诀诊,不管之前存不存在洞渤,反正就不跟默認的混了,自己只認指定的任務棧属瓣,當然您宪,如果你非要把taskAffinity指定自己的包名那也沒辦法,只是那沒撒意思嘛)
我們利用taskAffinity+taskAffinity指定非手機里面任何程序的包名的任務棧時奠涌,這個任務是可以容納多個activity的。比如現(xiàn)在有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一起出現(xiàn)的
android:taskAffinity="com.task.try"
/>
如上赐劣,我們的包名是 com.amqr.newtaskone嫉拐,然后我們給我們的activity指定了宿主任務棧的的名稱為 com.task.try 。這樣他就和系統(tǒng)默認的任務棧名稱不同了隆豹。
5.2椭岩、allowTaskReparenting
allowTaskReparenting:參數(shù)是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ā)現(xiàn)株搔,這時候進入的不是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ā)現(xiàn)翻诉,我們點擊啟動的app2炮姨,結果啟動的卻是app1,而且米丘,在運行列表里面看不到app2的存在剑令,只有一個app1。
里面發(fā)生的是這樣的一個過程拄查,
當我們啟動app1時:app1的任務棧com.amqr.taskaffinity1 正常作為一個任務棧進入系統(tǒng)吁津,
按下home鍵時:任務棧 com.amqr.taskaffinity1 成為了一個后臺任務棧
點擊app2時: 本來應該打開的是app2的界面,但是要打開的MainActivity2發(fā)現(xiàn)自身含有android:taskAffinity堕扶,而指定的宿主任務棧就是app1打開后就存在的任務棧 com.amqr.taskaffinity1 碍脏,那么這時候 com.amqr.taskaffinity1 就成為了 MainActivity2 的宿主任務棧。
按道理稍算,這個時候本來應該是 MainActivity2 入棧而且作為棧頂?shù)陌〉湮玻鞘聦崊s沒有這么發(fā)生。也就是說糊探,這個時候钾埂,MainActivity2沒有入棧,對的科平,沒有入棧褥紫。他是跑過來搞笑的,通知了一下別人可以變成前臺任務棧了瞪慧,然后自己沒有入棧髓考,具體詳情,不得而知弃酌。
實際上發(fā)生的事情是:com.amqr.taskaffinity1 由后臺任務棧轉(zhuǎn)為前臺任務棧氨菇,而那個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ā)現(xiàn)一樣坷随,還是貼一下對應的終端信息吧
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ā)現(xiàn)打開的是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)的根據(jù)包名定的默認任務棧破花,這個沒什么可說的谦趣。
但是當我們點擊打開 app1Other 頁面時,就多出來一個任務棧座每,棧名是我們指定的com.amqr.independent前鹅。
而且觀察運行程序的列表,發(fā)現(xiàn)多了個圖標峭梳,但是要知道舰绘,這個絕對不是多了一個進程,只是我們每多產(chǎn)生多一個任務棧延赌,任務列表就會多出來一個圖標者蠕。
進程不變
- 第二階段
緊接上面的環(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ā)現(xiàn)了,
圖片的任務列表是3個嘲玫,終端的日志任務棧看起來像出現(xiàn)了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的個數(shù)(已測)
至于為什么分開顯示漆魔,這個應該是終端顯示的一種表示方法矢炼,終端的具體規(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ā)現(xiàn),在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}
第一階段結束。
.
.
第二階段:
緊接著上面的操作,在上面的操作里面我們的指定的任務棧里面已經(jīng)有了兩個都是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參考
完。