ActivityRecord、TaskRecord、ActivityStack以及Activity启动模式详解

1.简介

先来张简单的关系图:


ActivityStack、TaskRecord、ActivityRecord关系图.png
  • 一个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,由此可以看出:ActivityRecordTaskRecord建立了联系。

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>用来保存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>,用来管理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对象:mHomeStackmFocusedStack,用来管理不同的任务。
  • ActivityStackSupervisor内部包含了创建ActivityStack对象的方法。

AMS初始化时会创建一个ActivityStackSupervisor对象。

2.5 总结

所以,实际上,他们的关系应该是这样的:


ActivityStack、TaskRecord、ActivityRecord、ActivityStackSupervisor关系图.png

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中的mHomeStackmHomeStack管理的是Launcher相关的任务。

桌面下的状态.png

3.2 从桌面启动一个Activity

从桌面启动一个APP,然后运行上面的命令,为了节省篇幅,这里和后面就不贴结果了,直接放图了。

从桌面点击图标启动一个AActivity,可以看到,会多了一个Stack #1,这个Stack #1就是ActivityStackSupervisor中的mFocusedStackmFocusedStack负责管理的是非Launcher相关的任务。同时也会创建一个新的ActivityRecordTaskRecordActivityRecord放到TaskRecord中,TaskRecord则放进mFocusedStack中。

启动A.png

3.3 默认模式从A启动B

然后,我们从AActivity中启动一个BActivity,可以看到会创建一个新的ActivityRecord然后放到已有的TaskRecord栈顶。

默认(standerd)模式启动B.png

3.4 从A启动B创建新栈

如果我们想启动的BActivity在一个新的栈中呢,我们可以用singleInstance的方式来启动BActivitysingleInstance后面也会讲到。这种方式会创建一个新的ActivityRecordTaskRecord,把ActivityRecord放到新的TaskRecord中去。

启动新Task.png

4. 流程分析

4.1 启动流程

这里对启动Activity过程中涉及到的ActivityStackTaskRecordActivityRecordActivityStackSupervisor进行简单的分析,实际上一张时序图就可以看明白了。相关的代码可以看上面的内容。

Activity启动UML类图.png

简单总结:
1.startActivity时首先会创建一个ActivityRecord
2.如果有需要,会创建一个TaskRecord,并把这个TaskRecord加入到ActivityStack中。
3.将ActivityRecord添加到TaskRecord的栈顶。

5. 启动模式

相信看完上面的介绍,现在再来看启动模式那是so easy了。

5.1 standerd

默认模式,每次启动Activity都会创建一个新的Activity实例。

比如:现在有个A Activity,我们在A上面启动B,再然后在B上面启动A,其过程如图所示:

standerd.png

5.2 singleTop

如果要启动的Activity已经在栈顶,则不会重新创建Activity,只会调用该该Activity的onNewIntent()方法。
如果要启动的Activity不在栈顶,则会重新创建该Activity的实例。

比如:现在有个A Activity,我们在A以standerd模式上面启动B,然后在B上面以singleTop模式启动A,其过程如图所示,这里会新创建一个A实例:

singleTop_A.png

如果在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,其过程如图所示:

singleTask.png

5.4 singleInstance

基本和singleTask一样,不同的是启动Activity时,首先要创建在一个新栈,然后创建该Activity实例并压入新栈中,新栈中只会存在这一个Activity实例。

比如:现在有个A Activity,我们在A以singleInstance模式上面启动B,其过程如图所示:

singleInstance.png

6.Intent的FLAG

另外,如果startActivity()时往Intent 中加入相应的标志来指定启动模式,这种方式的优先级会比在AndroidManifest中定义的优先级高;但是AndroidManifest中只能定义四种启动方式:standardsingleTopsingleTasksingleInstance,而Intentflag则有很多种。具体的可以看看文档,我们这里看看部分flag

  • FLAG_ACTIVITY_NEW_TASK :跟launchMode中的singleTask一样。
  • FLAG_ACTIVITY_SINGLE_TOP :跟launchMode中的singleTop一样。
  • FLAG_ACTIVITY_CLEAR_TOPlaunchMode中没有对应的值,如果要启动的Activity已经存在于栈中,则将所有位于它上面的Activity出栈。singleTask默认具有此标记位的效果。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容