寫在前面:Android APP有四種啟動模式——》標準模式(Standard)、棧頂復用模式(SingleTop)、棧內復用模式(SingleTask)、單例模式(SingleInstance),默認就是標準模式。啟動模式決定了Activity在任務棧內的存在方式,影響了Back返回鍵Activity返回的順序。啟動模式看起來簡單,但是實際上想要控制某些Activity的返回順序,需要結合兩個部分一起實現——》一是在AndroidManifest.xml中合理指定Activity的啟動模式;二是在使用Intent啟動Activity時傳入合理的啟動Flag。好,講了這么多其實就是想說APP啟動模式最大的作用是用來控制Activity的返回順序,它由兩部分共同進行控制,控制起來比較復雜,有必要好好學一學,把它搞清楚。
??開始之前我們先來明確一下概念,就是什么是任務棧:
任務棧:一個APP的所有Activity實例是用棧來進行管理的,當我們啟動一個Activity,這個Activity就會入棧處在棧頂,只有在棧頂的Activity是用戶當前可見的,按Back鍵棧頂的Activity就會出棧,如果棧里面還有Activity就繼續顯示棧頂Activity,沒有就直接退出APP,這就是為什么打開的Activity多了需要按多次Back鍵才能退出的原因。
注:1、Home鍵不會導致出棧操作,這也是為什么使用Recent鍵打開最近使用APP能接著之前的Activity頁面使用的原因。2、系統中存在多個任務棧,每個APP存在至少一個任務棧,不同的APP任務棧肯定不一樣。
下面從四種啟動模式開始講解。
一、標準模式(Standard)
??沒在AndroidManifest.xml指定Activity的啟動模式,模式就是標準模式。Standard 是 Android Activity 的默認啟動模式,每次啟動 Activity 時,都會創建一個新的實例,并放入當前任務棧(Task)的頂部。即使該 Activity 已經在任務棧中,也會創建一個新的實例,而不會復用已有實例。
下面畫張圖舉個例子看一下:同一個棧里有Activity1、Activity2,多次啟動Activity1。
可以看到每次啟動Actvity1都會重新創建,沒有復用之前的棧內實例。真實的棧可以使用如下adb命令查看:執行命令打印會很多,關注Task display areas in top down Z order:的相關字段即可,比如Task{ec5b36c就是一個已經存在的任務棧。
adb命令:dumpsys activity activitiesTask display areas in top down Z order:TaskDisplayArea DefaultTaskDisplayAreamPreferredTopFocusableRootTask=Task{ec5b36c #115 type=standard A=10158:com.htc.app_launch_mode U=0 visible=true mode=fullscreen translucent=false sz=2}mLastFocusedRootTask=Task{ec5b36c #115 type=standard A=10158:com.htc.app_launch_mode U=0 visible=true mode=fullscreen translucent=false sz=2}Application tokens in top down Z order:* Task{ec5b36c #115 type=standard A=10158:com.htc.app_launch_mode U=0 visible=true mode=fullscreen translucent=false sz=2}bounds=[0,0][1440,3120]* ActivityRecord{ed84d3d u0 com.htc.app_launch_mode/.MainActivity t115}* ActivityRecord{61c5b7d u0 com.htc.app_launch_mode/.MainActivity t115}* Task{6938da2 #1 type=home ?? U=0 visible=false mode=fullscreen translucent=true sz=1}bounds=[0,0][1440,3120]* Task{d8594e9 #112 type=home I=com.google.android.apps.nexuslauncher/.NexusLauncherActivity U=0 rootTaskId=1 visible=false mode=fullscreen translucent=true sz=1}bounds=[0,0][1440,3120]* ActivityRecord{92f632c u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity t112}
二、棧內復用模式(SingleTask)
??SingleTask 啟動模式意味著:在任務棧(Task)中只會存在一個該 Activity 的實例。如果 Activity 已經存在于任務棧中,則不會重新創建,而是復用已有實例,并調用它的 onNewIntent() 方法。復用時,該 Activity 之上的所有 Activity 都會被清除(類似“回退”操作)。
使用方式直接在AndroidManifest.xml中顯示指定launchMode即可,如下所示:
<activityandroid:name=".Activity2"android:exported="false"android:launchMode="singleTask"/><activityandroid:name=".MainActivity"android:exported="true"android:launchMode="singleTask"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity>
??假設現在有5個Activity,Activity1、Activity3是棧內復用模式,用下面的圖進行說明:
三、棧頂復用模式(SingleTop)
??這種模式的特點是——》如果 Activity 已經在棧頂,則不會新建實例,而是復用當前實例并調用 onNewIntent() 方法。如果 Activity 不在棧頂,則仍會創建新實例。不會清除任務棧中的其他 Activity,與 singleTask 不同。可以用下面這張圖來做一個形象的表示:假設有兩個Activity,Activity1、Activity2都是棧頂模式。
可以看到只有在棧頂的會被復用。
四、單例模式(SingleInstance)
??單例模式的特點——》Activity將獨占一個任務棧,且APP所有的任務棧中將只存在一個Activity實例對象。
用下面這張圖舉個例子:假設有三個Activity,Activity1、Activity2是非單例模式,Activity3是單例模式。
通過上圖可以看到,我們從Activity1開始,但是最終返回的最后一個界面卻變成了Activity3,Activity3再返回就是桌面了,這就是不同的啟動模式對Activity返回順序的影響,如果業務復雜,Activity很多,四種模式混在一起用,要準確控制Activity的返回順序就是一件比較困難的事情了。
五、Intent啟動Activity附帶的Flag
5.1 FLAG_ACTIVITY_NEW_TASK
??這個標志位應該是大家最熟悉的了,如下使用:
Intent intent = new Intent(this, TargetActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
??它最大的特點在于:當 Activity 不是從另一個 Activity 中啟動,而是從 Service、BroadcastReceiver 或 Application 啟動時,必須使用 FLAG_ACTIVITY_NEW_TASK,否則會導致報錯 android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag.因為Service、BroadcastReceiver 或 Application本身是不在任務棧中運行的,不存在任務棧,必須加上FLAG_ACTIVITY_NEW_TASK去新建任務棧。當然了如果APP已經存在了任務棧,那么就不會去創建新的任務棧,而是根據Activity的啟動模式合理復用或者入棧。
注意:FLAG_ACTIVITY_NEW_TASK是APP沒有任務棧的時候去新建,有了的話就不會。
所以啟動Activity都會加上這個Flag,不會有什么副作用,還能防止出錯,屬于萬金油了。示意圖如下:
5.2 FLAG_ACTIVITY_CLEAR_TASK
??這個FLAG的意思就是在啟動Activity之前,清空當前啟動它的任務棧,Standard、SingleTask、SingleTop三種模式完全服從FLAG_ACTIVITY_CLEAR_TASK,但是SingleInstance 卻不是這樣,它會影響這個FLAG的行為,具體來說分兩種:1、SingleInstance的Activity啟動其它模式的Activity。2、其它模式的Activity啟動SingleInstance的Activity。使用起來也很簡單:
Intent intent = new Intent(this, TargetActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
??下面畫一張圖來做個演示:三個Activity,Activity1、Activity3非單例模式,Activity2單例模式。
可以看到,Activity1啟動Activity2對應的是非單例模式Activity啟動單例模式Activity:已有任務棧Task1不會被清空,而是新建一個任務棧Task2啟動單例Activity。Activity2啟動Activity3對應單例模式Activity啟動非單例模式Activity:Task2沒有任何影響,Task1中的Activity1被清除,取而代之的是Activity3。
5.3 FLAG_ACTIVITY_MATCH_EXTERNAL
??所有的FLAG沒有寫完,后續會陸續補充完成。
5.4 FLAG_ACTIVITY_SINGLE_TOP
??
5.5 FLAG_ACTIVITY_MULTIPLE_TASK
??
5.6 FLAG_ACTIVITY_FORWARD_RESULT
??
5.7 FLAG_ACTIVITY_FORWARD_RESULT
??
5.8 FLAG_ACTIVITY_PREVIOUS_IS_TOP
??
5.9 FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
??
5.10 FLAG_ACTIVITY_BROUGHT_TO_FRONT
??
5.11 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
??
5.12 FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
??
5.13 FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
??
5.14 FLAG_ACTIVITY_NEW_DOCUMENT
??
5.15 FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
??
5.16 FLAG_ACTIVITY_NO_USER_ACTION
??
5.17 FLAG_ACTIVITY_REORDER_TO_FRONT
??
5.18 FLAG_ACTIVITY_NO_ANIMATION
??
5.19 FLAG_ACTIVITY_TASK_ON_HOME
??
5.20 FLAG_ACTIVITY_RETAIN_IN_RECENTS
??
5.21 FLAG_ACTIVITY_LAUNCH_ADJACENT
??