寫這篇文章是由於前幾天的一次面試,面試官說
SingleInstance
模式會新建一個桟,而SingleTask
不會.首先不說這個對不對(非要說對錯的話,那就是錯.),由於這句話是含糊不清的.桟?只的是返回桟? 仍是任務桟?有沒有考慮taskAffinity
屬性?因此籠統的那樣說是不對的.這篇文章一是爲了記錄,二是爲了說清楚----任務(Task)& 桟(返回桟,任務桟).html
桟(堆棧:stack)android
棧的基本特色:面試
先入後出,後入先出。瀏覽器
除頭尾節點以外,每一個元素有一個前驅,一個後繼。網絡
任務app
任務是指在執行特定做業時與用戶交互的一系列 Activity。 這些 Activity 按照各自的打開順序排列在堆棧(即返回棧)中。ide
返回桟
在官方文檔中,找不到關於返回桟的概念,可是按照官方文檔在描述時的語境,能夠理解爲響應Android返回鍵的順序隊列.這是站在用戶的直觀感覺上的描述.Android 內部的描述單位只有任務(Task).測試
任務桟
在官方文檔中沒有這個概念.我爲了方便理解,把任務
中按照桟
規則的順序隊列的 一系列 Activity 稱爲 任務棧
.ui
因爲 官方文檔中沒有詳細的說明什麼是返回桟
,在實際的開發中若是以棧爲基本度量單位
的話,很容易被本身繞暈,若是按照任務
爲基本度量單位的話就很容易理清楚 Android 四大啓動模式
了.我我的把返回棧理解爲這樣子的: 返回桟僅有一個(由於返回鍵只有一個),返回桟操做的基本元素是任務
,返回鍵操做的是返回桟中處於前臺的任務
,每一個任務
維護着一系列 Activity 組成的棧
以響應返回鍵.google
"standard"(默認模式)
默認。系統在啓動 Activity 的任務中建立 Activity 的新實例並向其傳送 Intent。Activity 能夠屢次實例化,而每一個實例都可屬於不一樣的任務,而且一個任務能夠擁有多個實例。
"singleTop"
若是當前任務的頂部已存在 Activity 的一個實例,則系統會經過調用該實例的 onNewIntent() 方法向其傳送 Intent,而不是建立 Activity 的新實例。Activity 能夠屢次實例化,而每一個實例都可屬於不一樣的任務,而且一個任務能夠擁有多個實例(但前提是位於返回棧頂部的 Activity 並非 Activity 的現有實例)。
例如,假設任務的返回棧包含根 Activity A 以及 Activity B、C 和位於頂部的 D(堆棧是 A-B-C-D;D 位於頂部)。收到針對 D 類 Activity 的 Intent。若是 D 具備默認的 "standard" 啓動模式,則會啓動該類的新實例,且堆棧會變成 A-B-C-D-D。可是,若是 D 的啓動模式是"singleTop",則 D 的現有實例會經過 onNewIntent() 接收 Intent,由於它位於堆棧的頂部;而堆棧仍爲 A-B-C-D。可是,若是收到針對 B 類 Activity 的 Intent,則會向堆棧添加 B 的新實例,即使其啓動模式爲 "singleTop" 也是如此。
注:爲某個 Activity 建立新實例時,用戶能夠按「返回」按鈕返回到前一個 Activity。 可是,當 Activity 的現有實例處理新 Intent 時,則在新 Intent 到達 onNewIntent() 以前,用戶沒法按「返回」按鈕返回到 Activity 的狀態。
"singleTask"
系統建立新任務並實例化位於新任務底部的 Activity。可是,若是該 Activity 的一個實例已存在於一個單獨的任務中,則系統會經過調用現有實例的 onNewIntent() 方法向其傳送 Intent,而不是建立新實例。一次只能存在 Activity 的一個實例。
注:儘管 Activity 在新任務中啓動,可是用戶按「返回」按鈕仍會返回到前一個 Activity。
"singleInstance".
與 "singleTask" 相同,只是系統不會將任何其餘 Activity 啓動到包含實例的任務中。該 Activity 始終是其任務惟一僅有的成員;由此 Activity 啓動的任何 Activity 均在單獨的任務中打開。
咱們再來看另外一示例,Android 瀏覽器應用聲明網絡瀏覽器 Activity 應始終在其本身的任務中打開(經過在 <activity> 元素中指定 singleTask 啓動模式)。這意味着,若是您的應用發出打開 Android 瀏覽器的 Intent,則其 Activity 與您的應用位於不一樣的任務中。相反,系統會爲瀏覽器啓動新任務,或者若是瀏覽器已有任務正在後臺運行,則會將該任務上移一層以處理新 Intent。
不管 Activity 是在新任務中啓動,仍是在與啓動 Activity 相同的任務中啓動,用戶按「返回」按鈕始終會轉到前一個 Activity。 可是,若是啓動指定 singleTask 啓動模式的 Activity,則當某後臺任務中存在該 Activity 的實例時,整個任務都會轉移到前臺。此時,返回棧包括上移到堆棧頂部的任務中的全部 Activity。 上圖顯示了這種狀況。
上圖 顯示如何將啓動模式爲「singleTask」的 Activity 添加到返回棧。 若是 Activity 已是某個擁有本身的返回棧的後臺任務的一部分,則整個返回棧也會上移到當前任務的頂部。
android:taskAffinity
屬性android:taskAffinity
與 Activity 有着親和關係的任務。從概念上講,具備相同親和關係的 Activity 歸屬同一任務(從用戶的角度來看,則是歸屬同一「應用」)。 任務的親和關係由其根 Activity 的親和關係肯定。
親和關係肯定兩件事 - Activity 更改到的父項任務(請參閱 allowTaskReparenting 屬性)和經過 FLAG_ACTIVITY_NEW_TASK 標誌啓動 Activity 時將用來容納它的任務。
默認狀況下,應用中的全部 Activity 都具備相同的親和關係。您能夠設置該屬性來以不一樣方式組合它們,甚至能夠將在不一樣應用中定義的 Activity 置於同一任務內。 要指定 Activity 與任何任務均無親和關係,請將其設置爲空字符串。
若是未設置該屬性,則 Activity 繼承爲應用設置的親和關係(請參閱 <application> 元素的 taskAffinity 屬性)。 應用默認親和關係的名稱是 <manifest> 元素設置的軟件包名稱。
SingleTask
與SingleInstance
的異同我寫了一個Demo測試了下:
當我把SingleTaskA
配置爲:
<activity android:name=".SingleTaskA" android:launchMode="singleTask" android:taskAffinity="com.didikee.temp"/>
在SingleTaskA
中啓動一個標準模式的StandardB
Activity:
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"; } }
補充下基類:
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(); }
獲得以下結果:
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
啓動了一個新的任務(Task)
,id爲27,以後在SingleTaskA中啓動StandardB
並無作其餘的操做,而是直接在剛剛SingleTaskA
啓動的任務
中添加.
如今我去掉 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
可見,默認是不會建立新的任務
的.
如今,把SingleTaskA 改成 SingleInstanceA,而後在其中能夠啓動StandardB
(上面有代碼),獲得以下結果:
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
新建了一個任務
id爲31,在 SingleInstanceA 中啓動 StandardB 仍是繼續添加在以前任務
中.
Android 是以任務
爲核心的,不要被棧帶偏了.
SingleInstance
必定會新建一個任務
,而且該任務
中僅包含一個實例.
SingleTask
默認不會建立新任務
,可是能夠經過taskAffinity
達到建立新任務
的目的,建立的任務
可添加其餘元素.
最後,說的不對的歡迎交流.