相關文章
Android系統啓動流程系列
Android應用進程系列
Android深刻四大組件系列
Android深刻解析AMS系列html
關於AMS,原計劃是隻寫一篇文章來介紹,可是AMS功能繁多,一篇文章的篇幅遠遠不夠。這一篇咱們接着來學習與AMS相關的ActivityTask和Activity棧管理。
java
ActivityStack從名稱來看是跟棧相關的類,其實它是一個管理類,用來管理系統全部Activity的各類狀態。它由ActivityStackSupervisor來進行管理的,而ActivityStackSupervisor在AMS中的構造方法中被建立。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.javaandroid
public ActivityManagerService(Context systemContext) {
...
mStackSupervisor = new ActivityStackSupervisor(this);
...
}複製代碼
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的堆棧。ide
經過ActivityStackSupervisor提供了獲取上述ActivityStack的方法,好比要獲取mFocusedStack,只須要調用ActivityStackSupervisor的getFocusedStack方法就能夠了:
frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java學習
ActivityStack getFocusedStack() {
return mFocusedStack;
}複製代碼
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.javathis
@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方法來進行切換動畫。spa
在ActivityStack中定義了一些特殊狀態的Activity,以下所示。.net
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。
ActivityStack中維護了不少ArrayList,這些ArrayList中的元素類型主要有ActivityRecord和TaskRecord,其中TaskRecord用來記錄Activity的Task。
ArrayList | 元素類型 | 說明 |
---|---|---|
mTaskHistory | TaskRecord | 全部沒有被銷燬的Task |
mLRUActivities | ActivityRecord | 正在運行的Activity,列表中的第一個條目是最近最少使用的元素 |
mNoAnimActivities | ActivityRecord | 不考慮轉換動畫的Activity |
mValidateAppTokens | TaskGroup | 用於與窗口管理器驗證應用令牌 |
咱們知道Activity是由任務棧來進行管理的,不過任務棧是一個假想的模型,並不真實的存在。棧管理就是創建在這個假想模型之上的,有了棧管理,咱們能夠對應用程序進行操做,應用能夠複用自身應用中以及其餘應用的Activity,節省了資源。好比咱們使用一款社交應用,這個應用的聯繫人詳情界面提供了聯繫人的郵箱,當咱們點擊郵箱時會跳到發送郵件的界面。
社交應用和系統Email中的Activity是處於不一樣應用程序進程的,而有了棧管理,就能夠把發送郵件界面放到社交應用中詳情界面所在棧的棧頂,來作到跨進程操做。
爲了更靈活的進行棧管理,Android系統提供了不少配置,下面分別對它們進行介紹。
Launch Mode都不會陌生,用於設定Activity的啓動方式,不管是哪一種啓動方式,所啓動的Activity都會位於Activity棧的棧頂。有如下四種:
Intent中定義了不少了FLAG,其中有幾個FLAG也能夠設定Activity的啓動方式,若是Launch Mode設定和FLAG設定的Activity的啓動方式有衝突,則以FLAG設定的爲準。
除了這三個FLAG,還有一些FLAG對咱們分析棧管理有些幫助。
接下來經過系統源碼來查看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,則也要建立一個新棧。
咱們能夠在AndroidManifest.xml設置android:taskAffinity,用來指定Activity但願歸屬的棧, 默認狀況下,同一個應用程序的全部的Activity都有着相同的taskAffinity。
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相關原創技術乾貨。
掃一掃下方二維碼或者長按識別二維碼,便可關注。