相關文章
Android系統啟動流程系列
Android應用程式系列
Android深入四大元件系列
Android深入解析AMS系列
前言
關於AMS,原計劃是隻寫一篇文章來介紹,但是AMS功能繁多,一篇文章的篇幅遠遠不夠。這一篇我們接著來學習與AMS相關的ActivityTask和Activity棧管理。
1.ActivityStack
ActivityStack從名稱來看是跟棧相關的類,其實它是一個管理類,用來管理系統所有Activity的各種狀態。它由ActivityStackSupervisor來進行管理的,而ActivityStackSupervisor在AMS中的構造方法中被建立。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public ActivityManagerService(Context systemContext) {
...
mStackSupervisor = new ActivityStackSupervisor(this);
...
}複製程式碼
1.1 ActivityStack的例項型別
ActivityStackSupervisor中有多種ActivityStack例項,如下所示。
frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
public final class ActivityStackSupervisor implements DisplayListener {
...
ActivityStack mHomeStack;
ActivityStack mFocusedStack;
private ActivityStack mLastFocusedStack;
...
}複製程式碼
mHomeStack用來儲存Launcher App的Activity的堆疊,mFocusedStack表示當前正在接收輸入或啟動下一個Activity的堆疊。mLastFocusedStack表示此前接收輸入的Activity的堆疊。
通過ActivityStackSupervisor提供了獲取上述ActivityStack的方法,比如要獲取mFocusedStack,只需要呼叫ActivityStackSupervisor的getFocusedStack方法就可以了:
frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
ActivityStack getFocusedStack() {
return mFocusedStack;
}複製程式碼
1.2 ActivityState
ActivityStack中通過列舉儲存了Activity的所有的狀態,如下所示。
frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
enum ActivityState {
INITIALIZING,
RESUMED,
PAUSING,
PAUSED,
STOPPING,
STOPPED,
FINISHING,
DESTROYING,
DESTROYED
}複製程式碼
通過名稱我們可以很輕易知道這些狀態所代表的意義。應用ActivityState的場景會有很多,比如下面的程式碼:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@Override
public void overridePendingTransition(IBinder token, String packageName,
int enterAnim, int exitAnim) {
...
if (self.state == ActivityState.RESUMED
|| self.state == ActivityState.PAUSING) {//1
mWindowManager.overridePendingAppTransition(packageName,
enterAnim, exitAnim, null);
}
Binder.restoreCallingIdentity(origId);
}
}複製程式碼
overridePendingTransition方法用於設定Activity的切換動畫,註釋1處可以看到只有ActivityState為RESUMED狀態或者PAUSING狀態時才會呼叫WMS型別的mWindowManager物件的overridePendingAppTransition方法來進行切換動畫。
1.3 特殊狀態的Activity
在ActivityStack中定義了一些特殊狀態的Activity,如下所示。
ActivityRecord mPausingActivity = null;//正在暫停的Activity
ActivityRecord mLastPausedActivity = null;//上一個已經暫停的Activity
ActivityRecord mLastNoHistoryActivity = null;//最近一次沒有歷史記錄的Activity
ActivityRecord mResumedActivity = null;//已經Resume的Activity
ActivityRecord mLastStartedActivity = null;//最近一次啟動的Activity
ActivityRecord mTranslucentActivityWaiting = null;//傳遞給convertToTranslucent方法的最上層的Activity複製程式碼
這些特殊的狀態都是ActivityRecord型別的,ActivityRecord用來記錄一個Activity的所有資訊。從棧的角度來說(Activity任務棧是一個假想的模型),一個或多個ActivityRecord會組成一個TaskRecord,TaskRecord用來記錄Activity的棧,而ActivityStack包含了一個或多個TaskRecord。
1.4 維護的ArrayList
ActivityStack中維護了很多ArrayList,這些ArrayList中的元素型別主要有ActivityRecord和TaskRecord,其中TaskRecord用來記錄Activity的Task。
ArrayList | 元素型別 | 說明 |
---|---|---|
mTaskHistory | TaskRecord | 所有沒有被銷燬的Task |
mLRUActivities | ActivityRecord | 正在執行的Activity,列表中的第一個條目是最近最少使用的元素 |
mNoAnimActivities | ActivityRecord | 不考慮轉換動畫的Activity |
mValidateAppTokens | TaskGroup | 用於與視窗管理器驗證應用令牌 |
2.Activity棧管理
我們知道Activity是由任務棧來進行管理的,不過任務棧是一個假想的模型,並不真實的存在。棧管理就是建立在這個假想模型之上的,有了棧管理,我們可以對應用程式進行操作,應用可以複用自身應用中以及其他應用的Activity,節省了資源。比如我們使用一款社交應用,這個應用的聯絡人詳情介面提供了聯絡人的郵箱,當我們點選郵箱時會跳到傳送郵件的介面。
社交應用和系統Email中的Activity是處於不同應用程式程式的,而有了棧管理,就可以把傳送郵件介面放到社交應用中詳情介面所在棧的棧頂,來做到跨程式操作。
為了更靈活的進行棧管理,Android系統提供了很多配置,下面分別對它們進行介紹。
2.1 Launch Mode
Launch Mode都不會陌生,用於設定Activity的啟動方式,無論是哪種啟動方式,所啟動的Activity都會位於Activity棧的棧頂。有以下四種:
- standerd:預設模式,每次啟動Activity都會建立一個新的Activity例項。
- singleTop:如果要啟動的Activity已經在棧頂,則不會重新建立Activity,同時該Activity的onNewIntent方法會被呼叫。如果要啟動的Activity不在棧頂,則會重新建立該Activity的例項。
- singleTask:如果要啟動的Activity已經存在於它想要歸屬的棧中,那麼不會建立該Activity例項,將棧中位於該Activity上的所有的Activity出棧,同時該Activity的onNewIntent方法會被呼叫。如果要啟動的Activity不存在於它想要歸屬的棧中,並且該棧存在,則會重新建立該Activity的例項。如果要啟動的Activity想要歸屬的棧不存在,則首先要建立一個新棧,然後建立該Activity例項並壓入到新棧中。
- singleInstance:和singleTask基本類似,不同的是啟動Activity時,首先要建立在一個新棧,然後建立該Activity例項並壓入新棧中,新棧中只會存在這一個Activity例項。
2.2 Intent的FLAG
Intent中定義了很多了FLAG,其中有幾個FLAG也可以設定Activity的啟動方式,如果Launch Mode設定和FLAG設定的Activity的啟動方式有衝突,則以FLAG設定的為準。
- FLAG_ACTIVITY_SINGLE_TOP:和Launch Mode中的singleTop效果是一樣的。
- FLAG_ACTIVITY_NEW_TASK:和Launch Mode中的singleTask效果是一樣的。
- FLAG_ACTIVITY_CLEAR_TOP:Launch Mode中沒有與此對應的模式,如果要啟動的Activity已經存在於棧中,則將所有位於它上面的Activity出棧。singleTask預設具有此標記位的效果。
除了這三個FLAG,還有一些FLAG對我們分析棧管理有些幫助。
- FLAG_ACTIVITY_NO_HISTORY:Activity一旦退出,就不會存在於棧中。同樣的,也可以在AndroidManifest.xml中設定“android:noHistory”。
- FLAG_ACTIVITY_MULTIPLE_TASK:需要和FLAG_ACTIVITY_NEW_TASK一同使用才有效果,系統會啟動一個新的棧來容納新啟動的Activity.
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:Activity不會被放入到“最近啟動的Activity”列表中。
- FLAG_ACTIVITY_BROUGHT_TO_FRONT:這個標誌位通常不是由應用程式中的程式碼設定的,而是Launch Mode為singleTask時,由系統自動加上的。
- FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY:這個標誌位通常不是由應用程式中的程式碼設定的,而是從歷史記錄中啟動的(長按Home鍵調出)。
- FLAG_ACTIVITY_CLEAR_TASK:需要和FLAG_ACTIVITY_NEW_TASK一同使用才有效果,用於清除與啟動的Activity相關棧的所有其他Activity。
接下來通過系統原始碼來檢視FLAG的應用,在Android深入四大元件(一)應用程式啟動過程(後篇)中講過,根Activity啟動時會呼叫AMS的startActivity方法,經過層層呼叫會呼叫ActivityStarter的startActivityUnchecked方法,如下面的時序圖所示。
frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor);//1
computeLaunchingTaskFlags();//2
computeSourceStack();
mIntent.setFlags(mLaunchFlags);//3
...
}複製程式碼
註釋1處用於初始化啟動Activity的各種配置,在初始化前會重置各種配置再進行配置,這些配置包括:ActivityRecord、Intent、TaskRecord和LaunchFlags(啟動的FLAG)等等。註釋2處的computeLaunchingTaskFlags方法用於計算出啟動的FLAG,並將計算的值賦值給mLaunchFlags。在註釋3處將mLaunchFlags設定給Intent,達到設定Activity的啟動方式的目的。接著來檢視computeLaunchingTaskFlags方法。
frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java
private void computeLaunchingTaskFlags() {
...
if (mInTask == null) {//1
if (mSourceRecord == null) {//2
if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {//3
Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
"Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
} else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {//4
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
} else if (mLaunchSingleInstance || mLaunchSingleTask) {//5
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
}
}複製程式碼
計算啟動的FLAG的邏輯比較複雜,這裡只擷取了一小部分,註釋1處的TaskRecord型別的mInTask為null時,說明Activity要加入的棧不存在。因此,這一小段程式碼主要解決的問題就是Activity要加入的棧不存在時如何計算出啟動的FLAG。註釋2處,ActivityRecord型別的mSourceRecord用於描述“初始Activity”,什麼是“初始Activity”呢?比如ActivityA啟動了ActivityB,ActivityA就是初始Activity。同時滿足註釋2和註釋3的條件則需要建立一個新棧。註釋4處,如果“初始Activity”所在的棧只允許有一個Activity例項,則也需要建立一個新棧。註釋5處,如果Launch Mode設定了singleTask或singleInstance,則也要建立一個新棧。
2.3 taskAffinity
我們可以在AndroidManifest.xml設定android:taskAffinity,用來指定Activity希望歸屬的棧, 預設情況下,同一個應用程式的所有的Activity都有著相同的taskAffinity。
taskAffinity在下面兩種情況時會產生效果。
- taskAffinity與FLAG_ACTIVITY_NEW_TASK或者singleTask配合。如果新啟動Activity的taskAffinity和棧的taskAffinity相同(棧的taskAffinity取決於根Activity的taskAffinity)則加入到該棧中。如果不同,就會建立新棧。
- taskAffinity與allowTaskReparenting配合。如果allowTaskReparenting為true,說明Activity具有轉移的能力。拿此前的郵件為例,當社交應用啟動了傳送郵件的Activity,此時傳送郵件的Activity是和社交應用處於同一個棧中。如果傳送郵件的Activity的allowTaskReparenting設定為true,此後郵件程式所在的棧位於前臺,這個時候傳送郵件的Activity就會由社交應用的棧中轉移到與它更親近的郵件程式(taskAffinity相同)所在的棧中。
接著通過系統原始碼來檢視taskAffinity的應用。ActivityStackSupervisor的findTaskLocked方法用於找到Activity最匹配的棧,最終會呼叫ActivityStack的findTaskLocked方法。
frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
void findTaskLocked(ActivityRecord target, FindTaskResult result) {
...
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {//1
final TaskRecord task = mTaskHistory.get(taskNdx);//2
...
else if (!isDocument && !taskIsDocument
&& result.r == null && task.canMatchRootAffinity()) {
if (task.rootAffinity.equals(target.taskAffinity)) {//3
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching affinity candidate!");
result.r = r;
result.matchedByRootAffinity = true;
}
} else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
}
}複製程式碼
這個方法的邏輯比較複雜,這裡擷取了和taskAffinity相關的部分。註釋1處遍歷mTaskHistory列表,列表的元素為TaskRecord,
用於儲存沒有被銷燬的Task。註釋2處得到某一個Task的資訊。註釋3處將Task的rootAffinity(初始的taskAffinity)和目標Activity的taskAffinity做對比,如果相同,則將FindTaskResult的matchedByRootAffinity 屬性設定為true,說明找到了匹配的Task。
參考資料
《深入理解Android卷二》
《深入理解Android核心設計思想》第二版
《Android開發藝術探索》
ActivityRecord、TaskRecord、ActivityStack
歡迎關注我的微信公眾號,第一時間獲得部落格更新提醒,以及更多成體系的Android相關原創技術乾貨。
掃一掃下方二維碼或者長按識別二維碼,即可關注。