Android解析ActivityManagerService(二)ActivityTask和Activity棧管理

相關文章
Android系統啓動流程系列
Android應用進程系列
Android深刻四大組件系列
Android深刻解析AMS系列html

前言

關於AMS,原計劃是隻寫一篇文章來介紹,可是AMS功能繁多,一篇文章的篇幅遠遠不夠。這一篇咱們接着來學習與AMS相關的ActivityTask和Activity棧管理。
java

1.ActivityStack

ActivityStack從名稱來看是跟棧相關的類,其實它是一個管理類,用來管理系統全部Activity的各類狀態。它由ActivityStackSupervisor來進行管理的,而ActivityStackSupervisor在AMS中的構造方法中被建立。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.javaandroid

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的堆棧。ide

經過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.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

1.3 特殊狀態的Activity

在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。

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在下面兩種狀況時會產生效果。

  1. taskAffinity與FLAG_ACTIVITY_NEW_TASK或者singleTask配合。若是新啓動Activity的taskAffinity和棧的taskAffinity相同(棧的taskAffinity取決於根Activity的taskAffinity)則加入到該棧中。若是不一樣,就會建立新棧。
  2. 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相關原創技術乾貨。
掃一掃下方二維碼或者長按識別二維碼,便可關注。

相關文章
相關標籤/搜索