摘要:如果具有默認(rèn)的啟動模式,則會啟動該類的新實例,且堆棧會變成。但是,如果的啟動模式是,則的現(xiàn)有實例會通過接收,因為它位于堆棧的頂部而堆棧仍為。上圖顯示如何將啟動模式為的添加到返回棧。任務(wù)的親和關(guān)系由其根的親和關(guān)系確定。
Android 啟動模式--任務(wù)(Task)--桟 的誤區(qū)
概念寫這篇文章是因為前幾天的一次面試,面試官說SingleInstance模式會新建一個桟,而SingleTask不會.首先不說這個對不對(非要說對錯的話,那就是錯.),因為這句話是含糊不清的.桟?只的是返回桟? 還是任務(wù)桟?有沒有考慮taskAffinity屬性?所以籠統(tǒng)的那樣說是不對的.這篇文章一是為了記錄,二是為了說清楚----任務(wù)(Task)& 桟(返回桟,任務(wù)桟).
桟(堆棧:stack)
棧的基本特點:
先入后出,后入先出。
除頭尾節(jié)點之外,每個元素有一個前驅(qū),一個后繼。
任務(wù)
任務(wù)是指在執(zhí)行特定作業(yè)時與用戶交互的一系列 Activity。 這些 Activity 按照各自的打開順序排列在堆棧(即返回棧)中。
返回桟
在官方文檔中,找不到關(guān)于返回桟的概念,但是按照官方文檔在描述時的語境,可以理解為響應(yīng)Android返回鍵的順序隊列.這是站在用戶的直觀感受上的描述.Android 內(nèi)部的描述單位只有任務(wù)(Task).
任務(wù)桟
在官方文檔中沒有這個概念.我為了方便理解,把任務(wù)中按照桟規(guī)則的順序隊列的 一系列 Activity 稱為 任務(wù)棧.
由于 官方文檔中沒有詳細(xì)的說明什么是返回桟,在實際的開發(fā)中如果以棧為基本度量單位的話,很容易被自己繞暈,如果按照任務(wù)為基本度量單位的話就很容易理清楚 Android 四大啟動模式了.我個人把返回棧理解為這樣子的: 返回桟僅有一個(因為返回鍵只有一個),返回桟操作的基本元素是任務(wù),返回鍵操作的是返回桟中處于前臺的任務(wù),每個任務(wù)維護(hù)著一系列 Activity 組成的棧以響應(yīng)返回鍵.
Activity 四大啟動模式"standard"(默認(rèn)模式)
默認(rèn)。系統(tǒng)在啟動 Activity 的任務(wù)中創(chuàng)建 Activity 的新實例并向其傳送 Intent。Activity 可以多次實例化,而每個實例均可屬于不同的任務(wù),并且一個任務(wù)可以擁有多個實例。
"singleTop"
如果當(dāng)前任務(wù)的頂部已存在 Activity 的一個實例,則系統(tǒng)會通過調(diào)用該實例的 onNewIntent() 方法向其傳送 Intent,而不是創(chuàng)建 Activity 的新實例。Activity 可以多次實例化,而每個實例均可屬于不同的任務(wù),并且一個任務(wù)可以擁有多個實例(但前提是位于返回棧頂部的 Activity 并不是 Activity 的現(xiàn)有實例)。
例如,假設(shè)任務(wù)的返回棧包含根 Activity A 以及 Activity B、C 和位于頂部的 D(堆棧是 A-B-C-D;D 位于頂部)。收到針對 D 類 Activity 的 Intent。如果 D 具有默認(rèn)的 "standard" 啟動模式,則會啟動該類的新實例,且堆棧會變成 A-B-C-D-D。但是,如果 D 的啟動模式是"singleTop",則 D 的現(xiàn)有實例會通過 onNewIntent() 接收 Intent,因為它位于堆棧的頂部;而堆棧仍為 A-B-C-D。但是,如果收到針對 B 類 Activity 的 Intent,則會向堆棧添加 B 的新實例,即便其啟動模式為 "singleTop" 也是如此。
注:為某個 Activity 創(chuàng)建新實例時,用戶可以按“返回”按鈕返回到前一個 Activity。 但是,當(dāng) Activity 的現(xiàn)有實例處理新 Intent 時,則在新 Intent 到達(dá) onNewIntent() 之前,用戶無法按“返回”按鈕返回到 Activity 的狀態(tài)。
"singleTask"
系統(tǒng)創(chuàng)建新任務(wù)并實例化位于新任務(wù)底部的 Activity。但是,如果該 Activity 的一個實例已存在于一個多帶帶的任務(wù)中,則系統(tǒng)會通過調(diào)用現(xiàn)有實例的 onNewIntent() 方法向其傳送 Intent,而不是創(chuàng)建新實例。一次只能存在 Activity 的一個實例。
注:盡管 Activity 在新任務(wù)中啟動,但是用戶按“返回”按鈕仍會返回到前一個 Activity。
"singleInstance".
與 "singleTask" 相同,只是系統(tǒng)不會將任何其他 Activity 啟動到包含實例的任務(wù)中。該 Activity 始終是其任務(wù)唯一僅有的成員;由此 Activity 啟動的任何 Activity 均在多帶帶的任務(wù)中打開。
我們再來看另一示例,Android 瀏覽器應(yīng)用聲明網(wǎng)絡(luò)瀏覽器 Activity 應(yīng)始終在其自己的任務(wù)中打開(通過在
無論 Activity 是在新任務(wù)中啟動,還是在與啟動 Activity 相同的任務(wù)中啟動,用戶按“返回”按鈕始終會轉(zhuǎn)到前一個 Activity。 但是,如果啟動指定 singleTask 啟動模式的 Activity,則當(dāng)某后臺任務(wù)中存在該 Activity 的實例時,整個任務(wù)都會轉(zhuǎn)移到前臺。此時,返回棧包括上移到堆棧頂部的任務(wù)中的所有 Activity。 上圖顯示了這種情況。
上圖 顯示如何將啟動模式為“singleTask”的 Activity 添加到返回棧。 如果 Activity 已經(jīng)是某個擁有自己的返回棧的后臺任務(wù)的一部分,則整個返回棧也會上移到當(dāng)前任務(wù)的頂部。
關(guān)于android:taskAffinity屬性參考:2017-03-14日摘自官方文檔(后期可能有變更以官網(wǎng)為準(zhǔn))
android:taskAffinity
與 Activity 有著親和關(guān)系的任務(wù)。從概念上講,具有相同親和關(guān)系的 Activity 歸屬同一任務(wù)(從用戶的角度來看,則是歸屬同一“應(yīng)用”)。 任務(wù)的親和關(guān)系由其根 Activity 的親和關(guān)系確定。
親和關(guān)系確定兩件事 - Activity 更改到的父項任務(wù)(請參閱 allowTaskReparenting 屬性)和通過 FLAG_ACTIVITY_NEW_TASK 標(biāo)志啟動 Activity 時將用來容納它的任務(wù)。
默認(rèn)情況下,應(yīng)用中的所有 Activity 都具有相同的親和關(guān)系。您可以設(shè)置該屬性來以不同方式組合它們,甚至可以將在不同應(yīng)用中定義的 Activity 置于同一任務(wù)內(nèi)。 要指定 Activity 與任何任務(wù)均無親和關(guān)系,請將其設(shè)置為空字符串。
如果未設(shè)置該屬性,則 Activity 繼承為應(yīng)用設(shè)置的親和關(guān)系(請參閱
測試 SingleTask與SingleInstance的異同參考:2017-03-14日摘自官方文檔(后期可能有變更以官網(wǎng)為準(zhǔn))
我寫了一個Demo測試了下:
當(dāng)我把SingleTaskA配置為:
在SingleTaskA中啟動一個標(biāo)準(zhǔn)模式的StandardBActivity:
public class SingleTaskA extends BaseLauncherModeActivity { @Override protected Class gotoActivity() { return StandardB.class; } @Override protected String setModeTextShow() { return Constant.SINGLE_TASK; } }
而在StandardB中,我一直啟動StandardB自己:
public class StandardB extends BaseLauncherModeActivity { @Override protected Class gotoActivity() { return StandardB.class; } @Override protected String setModeTextShow() { return Constant.STANDARD+"Repeat"; } }
補(bǔ)充下基類:
public abstract class BaseLauncherModeActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_base_launcher_mode); String text = setModeTextShow(); Button button = (Button) findViewById(R.id.bt); button.setText("模式: "+text); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Class aClass = gotoActivity(); if (aClass==null)return; startActivity(new Intent(getApplicationContext(),aClass)); } }); int taskId = getTaskId(); Log.d(Constant.TAG,"taskId: "+taskId); } protected abstract Class gotoActivity(); protected abstract String setModeTextShow(); }
得到如下結(jié)果:
03-14 10:32:32.287 22561-22561/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 26 03-14 10:32:37.691 22561-22561/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 26 03-14 10:32:42.120 22561-22561/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 27 03-14 10:32:45.961 22561-22561/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 27 03-14 10:32:52.105 22561-22561/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 27 03-14 10:32:53.531 22561-22561/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 27 03-14 10:32:55.748 22561-22561/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 27 03-14 10:32:56.763 22561-22561/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 27
可以看到,SingleTaskA啟動了一個新的任務(wù)(Task),id為27,之后在SingleTaskA中啟動StandardB并沒有做其他的操作,而是直接在剛剛SingleTaskA啟動的任務(wù)中添加.
現(xiàn)在我去掉 taskAffinity屬性的定義,得到:
03-14 16:42:10.604 31062-31062/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 29 03-14 16:42:15.261 31062-31062/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 29 03-14 16:42:16.865 31062-31062/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 29 03-14 16:42:19.197 31062-31062/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 29 03-14 16:42:20.232 31062-31062/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 29 03-14 16:42:21.049 31062-31062/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 29 03-14 16:42:22.180 31062-31062/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 29
可見,默認(rèn)是不會創(chuàng)建新的任務(wù)的.
現(xiàn)在,把SingleTaskA 改為 SingleInstanceA,然后在其中可以啟動StandardB(上面有代碼),得到如下結(jié)果:
03-14 16:46:59.669 2273-2273/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 30 03-14 16:47:09.367 2273-2273/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 30 03-14 16:47:10.602 2273-2273/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 31 03-14 16:47:12.013 2273-2273/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 30 03-14 16:47:14.438 2273-2273/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 30 03-14 16:47:15.429 2273-2273/com.didikee.androidlaunchermode D/AndroidLauncherMode: taskId: 30
可見,SingleInstanceA新建了一個任務(wù)id為31,在 SingleInstanceA 中啟動 StandardB 還是繼續(xù)添加在之前任務(wù)中.
總結(jié):Android 是以任務(wù)為核心的,不要被棧帶偏了.
SingleInstance 一定會新建一個任務(wù),并且該任務(wù)中僅包含一個實例.
SingleTask默認(rèn)不會創(chuàng)建新任務(wù),但是可以通過taskAffinity達(dá)到創(chuàng)建新任務(wù)的目的,創(chuàng)建的任務(wù)可添加其他元素.
最后,說的不對的歡迎交流.
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/66814.html
閱讀 1035·2022-07-19 10:19
閱讀 1804·2021-09-02 15:15
閱讀 1021·2019-08-30 15:53
閱讀 2665·2019-08-30 13:45
閱讀 2664·2019-08-26 13:57
閱讀 1994·2019-08-26 12:13
閱讀 1015·2019-08-26 10:55
閱讀 556·2019-08-26 10:46