ActivityRecord、TaskRecord、ActivityStack以及Activity啟動模式詳解

HanDrush發表於2019-03-21

1.簡介

先來張簡單的關係圖:

在這裡插入圖片描述

ActivityStack、TaskRecord、ActivityRecord關係圖

  • 一個ActivityRecord對應一個Activity,儲存了一個Activity的所有資訊;但是一個Activity可能會有多個ActivityRecord,因為Activity可以被多次啟動,這個主要取決於其啟動模式。
  • 一個TaskRecord由一個或者多個ActivityRecord組成,這就是我們常說的任務棧,具有後進先出的特點。
  • ActivityStack則是用來管理TaskRecord的,包含了多個TaskRecord。

下面進入詳細的程式碼分析,本文原始碼基於android 27。

2.程式碼分析

2.1 ActivityRecord

ActivityRecord,原始碼中的註釋介紹:An entry in the history stack, representing an activity. 翻譯:歷史棧中的一個條目,代表一個activity。

    frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java
    
    final class ActivityRecord extends ConfigurationContainer implements AppWindowContainerListener {

        final ActivityManagerService service; // owner
        final IApplicationToken.Stub appToken; // window manager token
        AppWindowContainerController mWindowContainerController;
        final ActivityInfo info; // all about me
        final ApplicationInfo appInfo; // information about activity's app
        
        //省略其他成員變數
        
        //ActivityRecord所在的TaskRecord
        private TaskRecord task;        // the task this is in.
        
        //構造方法,需要傳遞大量資訊
        ActivityRecord(ActivityManagerService _service, ProcessRecord _caller, int _launchedFromPid,
                       int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
                       ActivityInfo aInfo, Configuration _configuration,
                       com.android.server.am.ActivityRecord _resultTo, String _resultWho, int _reqCode,
                       boolean _componentSpecified, boolean _rootVoiceInteraction,
                       ActivityStackSupervisor supervisor, ActivityOptions options,
                       com.android.server.am.ActivityRecord sourceRecord) {
        
        }
    }
複製程式碼
  • 實際上,ActivityRecord中存在著大量的成員變數,包含了一個Activity的所有資訊。
  • ActivityRecord中的成員變數task表示其所在的TaskRecord,由此可以看出:ActivityRecord與TaskRecord建立了聯絡。

startActivity()時會建立一個ActivityRecord:

    frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java

    class ActivityStarter {
        private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
                                  String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
                                  IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                                  IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
                                  String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
                                  ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
                                  com.android.server.am.ActivityRecord[] outActivity, TaskRecord inTask) {

            //其他程式碼略
            
            ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
                    callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
                    resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
                    mSupervisor, options, sourceRecord);
                    
            //其他程式碼略
        }
    }
複製程式碼

2.2 TaskRecord

TaskRecord,內部維護一個ArrayList用來儲存ActivityRecord。

    frameworks/base/services/core/java/com/android/server/am/TaskRecord.java
    
    final class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
        final int taskId;       //任務ID
        final ArrayList<ActivityRecord> mActivities;   //使用一個ArrayList來儲存所有的ActivityRecord
        private ActivityStack mStack;   //TaskRecord所在的ActivityStack
        
        //構造方法
        TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
                   IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor, int type) {
            
        }
        
        //新增Activity到頂部
        void addActivityToTop(com.android.server.am.ActivityRecord r) {
            addActivityAtIndex(mActivities.size(), r);
        }
        
        //新增Activity到指定的索引位置
        void addActivityAtIndex(int index, ActivityRecord r) {
            //...

            r.setTask(this);//為ActivityRecord設定TaskRecord,就是這裡建立的聯絡

            //...
            
            index = Math.min(size, index);
            mActivities.add(index, r);//新增到mActivities
            
            //...
        }

        //其他程式碼略
    }
複製程式碼
  • 可以看到TaskRecord中使用了一個ArrayList來儲存所有的ActivityRecord。
  • 同樣,TaskRecord中的mStack表示其所在的ActivityStack。

startActivity()時也會建立一個TaskRecord:

    frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java
    
    class ActivityStarter {

        private int setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate, int preferredLaunchStackId, ActivityStack topStack) {
            mTargetStack = computeStackFocus(mStartActivity, true, mLaunchBounds, mLaunchFlags, mOptions);

            if (mReuseTask == null) {
                //建立一個createTaskRecord,實際上是呼叫ActivityStack裡面的createTaskRecord()方法,ActivityStack下面會講到
                final TaskRecord task = mTargetStack.createTaskRecord(
                        mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
                        mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
                        mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
                        mVoiceInteractor, !mLaunchTaskBehind /* toTop */, mStartActivity.mActivityType);

                //其他程式碼略
            }
        }
    }
複製程式碼

2.3 ActivityStack

ActivityStack,內部維護了一個ArrayList,用來管理TaskRecord。

    frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
    
    class ActivityStack<T extends StackWindowController> extends ConfigurationContainer implements StackWindowListener {

        private final ArrayList<TaskRecord> mTaskHistory = new ArrayList<>();//使用一個ArrayList來儲存TaskRecord

        final int mStackId;

        protected final ActivityStackSupervisor mStackSupervisor;//持有一個ActivityStackSupervisor,所有的執行中的ActivityStacks都通過它來進行管理
        
        //構造方法
        ActivityStack(ActivityStackSupervisor.ActivityDisplay display, int stackId,
                      ActivityStackSupervisor supervisor, RecentTasks recentTasks, boolean onTop) {

        }
        
        TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
                                    IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                                    boolean toTop, int type) {
                                    
            //建立一個task
            TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession, voiceInteractor, type);
            
            //將task新增到ActivityStack中去
            addTask(task, toTop, "createTaskRecord");

            //其他程式碼略

            return task;
        }
        
        //新增Task
        void addTask(final TaskRecord task, final boolean toTop, String reason) {

            addTask(task, toTop ? MAX_VALUE : 0, true /* schedulePictureInPictureModeChange */, reason);

            //其他程式碼略
        }

        //新增Task到指定位置
        void addTask(final TaskRecord task, int position, boolean schedulePictureInPictureModeChange,
                     String reason) {
            mTaskHistory.remove(task);//若存在,先移除
            
            //...
            
            mTaskHistory.add(position, task);//新增task到mTaskHistory
            task.setStack(this);//為TaskRecord設定ActivityStack

            //...
        }
        
        //其他程式碼略
    }
複製程式碼
  • 可以看到ActivityStack使用了一個ArrayList來儲存TaskRecord。
  • 另外,ActivityStack中還持有ActivityStackSupervisor物件,這個是用來管理ActivityStacks的。

ActivityStack是由ActivityStackSupervisor來建立的,實際ActivityStackSupervisor就是用來管理ActivityStack的,繼續看下面的ActivityStackSupervisor分析。

2.4 ActivityStackSupervisor

ActivityStackSupervisor,顧名思義,就是用來管理ActivityStack的。

    frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
    
    public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener {

        ActivityStack mHomeStack;//管理的是Launcher相關的任務

        ActivityStack mFocusedStack;//管理非Launcher相關的任務
        
        //建立ActivityStack
        ActivityStack createStack(int stackId, ActivityStackSupervisor.ActivityDisplay display, boolean onTop) {
            switch (stackId) {
                case PINNED_STACK_ID:
                    //PinnedActivityStack是ActivityStack的子類
                    return new PinnedActivityStack(display, stackId, this, mRecentTasks, onTop);
                default:
                    //建立一個ActivityStack
                    return new ActivityStack(display, stackId, this, mRecentTasks, onTop);
            }
        }

    }
複製程式碼
  • ActivityStackSupervisor內部有兩個不同的ActivityStack物件:mHomeStack、mFocusedStack,用來管理不同的任務。
  • ActivityStackSupervisor內部包含了建立ActivityStack物件的方法。

AMS初始化時會建立一個ActivityStackSupervisor物件。

2.5 總結

所以,實際上,他們的關係應該是這樣的:

ActivityRecord、TaskRecord、ActivityStack以及Activity啟動模式詳解

ActivityStack、TaskRecord、ActivityRecord、ActivityStackSupervisor關係圖

3. 場景分析

下面通過啟動Activity的程式碼來分析一下:

3.1 桌面

首先,我們看下處於桌面時的狀態,執行命令:

adb shell dumpsys activity
複製程式碼

結果如下:

ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
Display #0 (activities from top to bottom):
  Stack #0:
  
  //中間省略其他...
  
    Task id #102
    
  //中間省略其他...
  
      TaskRecord{446ae9e #102 I=com.google.android.apps.nexuslauncher/.NexusLauncherActivity U=0 StackId=0 sz=1}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.google.android.apps.nexuslauncher/.NexusLauncherActivity }
        Hist #0: ActivityRecord{54fa22 u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity t102}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.google.android.apps.nexuslauncher/.NexusLauncherActivity }
          ProcessRecord{19c7c43 2203:com.google.android.apps.nexuslauncher/u0a22}

    Running activities (most recent first):
      TaskRecord{446ae9e #102 I=com.google.android.apps.nexuslauncher/.NexusLauncherActivity U=0 StackId=0 sz=1}
        Run #0: ActivityRecord{54fa22 u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity t102}

    mResumedActivity: ActivityRecord{54fa22 u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity t102}
    
//省略其他
複製程式碼

實際上就是如下圖所示的結構,這裡的Stack #0就是ActivityStackSupervisor中的mHomeStack,mHomeStack管理的是Launcher相關的任務。

ActivityRecord、TaskRecord、ActivityStack以及Activity啟動模式詳解

桌面下的狀態

3.2 從桌面啟動一個Activity

從桌面啟動一個APP,然後執行上面的命令,為了節省篇幅,這裡和後面就不貼結果了,直接放圖了。

從桌面點選圖示啟動一個AActivity,可以看到,會多了一個Stack #1,這個Stack #1就是ActivityStackSupervisor中的mFocusedStack,mFocusedStack負責管理的是非Launcher相關的任務。同時也會建立一個新的ActivityRecord和TaskRecord,ActivityRecord放到TaskRecord中,TaskRecord則放進mFocusedStack中。

ActivityRecord、TaskRecord、ActivityStack以及Activity啟動模式詳解

啟動A

3.3 預設模式從A啟動B

然後,我們從AActivity中啟動一個BActivity,可以看到會建立一個新的ActivityRecord然後放到已有的TaskRecord棧頂。

ActivityRecord、TaskRecord、ActivityStack以及Activity啟動模式詳解

預設(standerd)模式啟動B

3.4 從A啟動B建立新棧

如果我們想啟動的BActivity在一個新的棧中呢,我們可以用singleInstance的方式來啟動BActivity。singleInstance後面也會講到。這種方式會建立一個新的ActivityRecord和TaskRecord,把ActivityRecord放到新的TaskRecord中去。

ActivityRecord、TaskRecord、ActivityStack以及Activity啟動模式詳解

啟動新Task

4. 流程分析

4.1 啟動流程

這裡對啟動Activity過程中涉及到的ActivityStack、TaskRecord、ActivityRecord、ActivityStackSupervisor進行簡單的分析,實際上一張時序圖就可以看明白了。相關的程式碼可以看上面的內容。

ActivityRecord、TaskRecord、ActivityStack以及Activity啟動模式詳解

Activity啟動UML類圖

簡單總結: 1.startActivity時首先會建立一個ActivityRecord。 2.如果有需要,會建立一個TaskRecord,並把這個TaskRecord加入到ActivityStack中。 3.將ActivityRecord新增到TaskRecord的棧頂。

5. 啟動模式

相信看完上面的介紹,現在再來看啟動模式那是so easy了。

5.1 standerd

預設模式,每次啟動Activity都會建立一個新的Activity例項。

比如:現在有個A Activity,我們在A上面啟動B,再然後在B上面啟動A,其過程如圖所示:

ActivityRecord、TaskRecord、ActivityStack以及Activity啟動模式詳解
standerd

5.2 singleTop

如果要啟動的Activity已經在棧頂,則不會重新建立Activity,只會呼叫該該Activity的onNewIntent()方法。 如果要啟動的Activity不在棧頂,則會重新建立該Activity的例項。

比如:現在有個A Activity,我們在A以standerd模式上面啟動B,然後在B上面以singleTop模式啟動A,其過程如圖所示,這裡會新建立一個A例項:

ActivityRecord、TaskRecord、ActivityStack以及Activity啟動模式詳解

singleTop_A

如果在B上面以singleTop模式啟動B的話,則不會重新建立B,只會呼叫onNewIntent()方法,其過程如圖所示:

singleTop_B.png

5.3 singleTask

如果要啟動的Activity已經存在於它想要歸屬的棧中,那麼不會建立該Activity例項,將棧中位於該Activity上的所有的Activity出棧,同時該Activity的onNewIntent()方法會被呼叫。 如果要啟動的Activity不存在於它想要歸屬的棧中,並且該棧存在,則會建立該Activity的例項。 如果要啟動的Activity想要歸屬的棧不存在,則首先要建立一個新棧,然後建立該Activity例項並壓入到新棧中。

比如:現在有個A Activity,我們在A以standerd模式上面啟動B,然後在B上面以singleTask模式啟動A,其過程如圖所示:

ActivityRecord、TaskRecord、ActivityStack以及Activity啟動模式詳解

singleTask

5.4 singleInstance

基本和singleTask一樣,不同的是啟動Activity時,首先要建立在一個新棧,然後建立該Activity例項並壓入新棧中,新棧中只會存在這一個Activity例項。

比如:現在有個A Activity,我們在A以singleInstance模式上面啟動B,其過程如圖所示:

ActivityRecord、TaskRecord、ActivityStack以及Activity啟動模式詳解

singleInstance

6.Intent的FLAG

另外,如果startActivity()時往Intent 中加入相應的標誌來指定啟動模式,這種方式的優先順序會比在AndroidManifest中定義的優先順序高;但是AndroidManifest中只能定義四種啟動方式:standard、singleTop、singleTask、singleInstance,而Intent的flag則有很多種。具體的可以看看文件,我們這裡看看部分flag:

  • FLAG_ACTIVITY_NEW_TASK :跟launchMode中的singleTask一樣。
  • FLAG_ACTIVITY_SINGLE_TOP :跟launchMode中的singleTop一樣。
  • FLAG_ACTIVITY_CLEAR_TOP :launchMode中沒有對應的值,如果要啟動的Activity已經存在於棧中,則將所有位於它上面的Activity出棧。singleTask預設具有此標記位的效果。

相關文章