android8.1.0系统_ActivityRecord/TaskRecord/ActivityStack/ActivityStackSupervisor原理

133 阅读13分钟

 一,整体框架

1.1 调用链路时序图

参考android系统_app冷启动流程分析-CSDN博客 【1.1 APP冷启动时序图】

文章深入介绍了 Android 系统中 ActivityManagerService 里的 ActivityRecord、TaskRecord、ActivityStack 三者的作用及关系。ActivityRecord 是 Activity 在 AMS 中的代表,伴随 Activity 启动与终止。TaskRecord 即任务栈,是一组 Activity 的集合,有多种启动模式和切换方式。ActivityStack 用于管理 TaskRecord,系统中有 Launcher 和非 Launcher 的 ActivityStack,后者在系统启动首个 app 时创建。三者相互关联,贯穿 AMS 对 Activity 的管理逻辑。

1.2 框架结构图

​编辑

1.2.1. 小结

1)一个ActivityRecord对应一个Activity,保存了一个Activity的所有信息;但是一个Activity可能会有多个ActivityRecord,因为Activity可以被多次启动,这个主要取决于其启动模式。
2)一个TaskRecord由一个或者多个ActivityRecord组成,这就是我们常说的任务栈,具有后进先出的特点。
3)ActivityStack则是用来管理TaskRecord的,包含了多个TaskRecord

4)ActivityStackSupervisor则是用来管理ActivityStack的,包含了多个ActivityStack,一部手机有且仅有一个ActivityStackSupervisor

二,原理分析

2.1 ActivityStackSupervisor源码分析

/Volumes/aosp/android-8.1.0_r52/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

public ActivityStackSupervisor(ActivityManagerService service, Looper looper) {
        mService = service;
        mHandler = new ActivityStackSupervisorHandler(looper);
        mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext, looper);
        mKeyguardController = new KeyguardController(service, this);
    }

/** The stack containing the launcher app. Assumed to always be attached to
     * Display.DEFAULT_DISPLAY. */
    ActivityStack mHomeStack;

    /** The stack currently receiving input or launching the next activity. */
    ActivityStack mFocusedStack;

2.1.1 小结

1)ActivityStackSupervisor是在AMS启动的时候创建的

2)ActivityStackSupervisor内部有两个不同的ActivityStack对象:mHomeStack、mFocusedStack,mLastFocusedStack,用来管理不同的任务。

3)ActivityStack mHomeStack //管理桌面的stack
ActivityStack mFocusedStack //管理当前聚焦stack
ActivityStack mLastFocusedStack //管理正在切换, 很少用到。

3)ActivityStack mHomeStack //管理桌面的stack
ActivityStack mFocusedStack //管理当前聚焦stack
ActivityStack mLastFocusedStack //管理正在切换, 很少用到。

2.2 ActivityStack源码分析

/Volumes/aosp/android-8.1.0_r52/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

ActivityStack(ActivityStackSupervisor.ActivityDisplay display, int stackId,
            ActivityStackSupervisor supervisor, RecentTasks recentTasks, boolean onTop) {
        mStackSupervisor = supervisor;
        mService = supervisor.mService;
        mHandler = new ActivityStackHandler(mService.mHandler.getLooper());
        mWindowManager = mService.mWindowManager;
        mStackId = stackId;
        mCurrentUser = mService.mUserController.getCurrentUserIdLocked();
        mRecentTasks = recentTasks;
        mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
                ? new LaunchingTaskPositioner() : null;
        mTmpRect2.setEmpty();
        mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
                mTmpRect2);
        mStackSupervisor.mStacks.put(mStackId, this);
        postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
    }

2.2.1 小结

1)ActivityStack,内部维护了一个ArrayList,用来管理TaskRecord
2)ActivityStack中还持有ActivityStackSupervisor对象,这个是用来管理ActivityStacks的

ActivityStack的说法听起来很容易和我们常说的任务栈混淆,很多人可能看到它的第一个反应是它就是我们的任务栈,然而并非如此,我们知道系统中可能同时有多个Task,一般前台就一个Task和用户进行交互,而后台中可能有多个Task存在,前后台的Task可以进行切换,AMS为了方便的管理这些Task而引入了ActivityStack,在ActivityStack内部通过ArrayList维护了一组TaskRecord。一般来说Launcher的Task属于单独的一个ActivityStack,称为Home Activity Stack,System UI如rencentActivity的Task属于一个单独ActivityStack,其他App的Task属于另一个ActivityStack。

2.3 TaskRecord源码分析

/Volumes/aosp/android-8.1.0_r52/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即任务栈,或者叫返回栈(back Stack),Task实际上是指执行一个特定任务时和用户进行交互的一组activity,什么意思呢?比如我们为了完成用微信发送消息这个任务,而需要打开首页的联系人列表,然后选择一个联系人进入聊天界面发送消息,这两个activity就可以说是一个Task,但如果我们说要发送图片,那么还会去打开选择照片的activity,这时候Task就是三个activity,所以说Task是一个动态的概念,但不管怎么样,我们总成为它是一组activity的集合,这个集合在AMS中就是TaskRecord,它和数据结构中的Stack结构类似,也是先进后出,当我们打开App中的一个activity1时,它对应的ActivityRecord被添加到TaskRecord中,再在activit1中打开第二个页面activity2,在activity2中打开activity3,同样它们的ActivityRecord也会被添加到TaskRecord,当我们按下返回键,activity3先从TaskRecord中弹出来,依次类推,当task中所有的activity被弹出后,这个task也就销毁了。最终它的结构如下图所示:

 ​编辑

Task切换

一般情况下,我们可以将打开的一个app看做是一个TaskRecord,当我们先后打开app A的两个页面A和B,这时候在其对应的TaskRecord中就有两个ActivityRecord的记录,当我们按下home键后,然后打开另一个app B的C和D页面,这时候app A的TaskRecord就到了后台中,它内部的两个activity此时都处于停止状态,而app B的TaskRecord此时处于前台和用户交互的状态,它的TaskRecord中也有两个ActivityRecord的记录,如果我们再按下home键,点击app A,这时候我们发现app A停留在B页面,也就是说app A的TaskRecord又作为一个整体从后台切换到了前台,它的状态和之前是一样的。系统可以同时管理多个后台的TaskRecord,但是如果用户打开的太多app在后台中,系统可能会回收一部分。

打开多个app时,AMS中就存在了多个TaskRecord,我们可以在这些TaskRecord之间进行切换,当我打开Email应用后可以在编辑Email的过程中打开相册应用来选择图片,选择完成后可以继续回到Email中编辑邮件。这个是Task切换带来的便利

2.4 ActivityRecord源码分析

/Volumes/aosp/android-8.1.0_r52/frameworks/base/services/core/java/com/android/server/am/ActivityRecord.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);
                    
            //其他代码略
        }
    }

startActivity的时候会创建一个ActivityRecord,来表示一个activity

三,情景分析

3.1 首先,我们看下处于桌面时的状态

运行命令:

adb shell dumpsys activity

ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)
Recent tasks:
* Recent #0: TaskRecord{dcb575e #12 I=com.android.launcher3/.Launcher U=0 StackId=0 sz=1}
* Recent #1: TaskRecord{65933c4 #15 A=com.android.systemui U=0 StackId=5 sz=1}
实际上就是如下图所示的结构,这里的Stack #0就是ActivityStackSupervisor中的mHomeStack,mHomeStack管理的是Launcher相关的任务。

Display #0 (activities from top to bottom):
  

  Stack #0:
  mFullscreen=true
  isSleeping=false
  mBounds=null
    Task id #12
    mFullscreen=true
    mBounds=null
    mMinWidth=-1
    mMinHeight=-1
    mLastNonFullscreenBounds=null
      TaskRecord{dcb575e #12 I=com.android.launcher3/.Launcher U=0 StackId=0 sz=1}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.launcher3/.Launcher }
        Hist #0: ActivityRecord{815643d u0 com.android.launcher3/.Launcher t12}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.launcher3/.Launcher }
          ProcessRecord{135de8b 3664:com.android.launcher3/u0a16}

    Running activities (most recent first):
      TaskRecord{dcb575e #12 I=com.android.launcher3/.Launcher U=0 StackId=0 sz=1}
        Run #0: ActivityRecord{815643d u0 com.android.launcher3/.Launcher t12}

  Stack #5:
  mFullscreen=true
  isSleeping=false
  mBounds=null
    Task id #15
    mFullscreen=true
    mBounds=null
    mMinWidth=-1
    mMinHeight=-1
    mLastNonFullscreenBounds=null
      TaskRecord{65933c4 #15 A=com.android.systemui U=0 StackId=5 sz=1}
      Intent { flg=0x10804000 cmp=com.android.systemui/.recents.RecentsActivity }
        Hist #0: ActivityRecord{445e887 u0 com.android.systemui/.recents.RecentsActivity t15}
          Intent { flg=0x10804000 cmp=com.android.systemui/.recents.RecentsActivity }
          ProcessRecord{ede67cc 1940:com.android.systemui/u0a25}

    Running activities (most recent first):
      TaskRecord{65933c4 #15 A=com.android.systemui U=0 StackId=5 sz=1}
        Run #0: ActivityRecord{445e887 u0 com.android.systemui/.recents.RecentsActivity t15}

​编辑

3.2 从桌面启动一个Activity

Display #0 (activities from top to bottom):
  Stack #1:
  mFullscreen=true
  isSleeping=false
  mBounds=null
    Task id #16
    mFullscreen=true
    mBounds=null
    mMinWidth=-1
    mMinHeight=-1
    mLastNonFullscreenBounds=null
      TaskRecord{2bef07f #16 A=com.android.messaging U=0 StackId=1 sz=1}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 pkg=com.android.messaging cmp=com.android.messaging/.ui.conversationlist.ConversationListActivity }
        Hist #0: ActivityRecord{d0283af u0 com.android.messaging/.ui.conversationlist.ConversationListActivity t16}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 pkg=com.android.messaging cmp=com.android.messaging/.ui.conversationlist.ConversationListActivity bnds=[317,2105][586,2385] }
          ProcessRecord{cf56e52 6157:com.android.messaging/u0a62}

    Running activities (most recent first):
      TaskRecord{2bef07f #16 A=com.android.messaging U=0 StackId=1 sz=1}
        Run #0: ActivityRecord{d0283af u0 com.android.messaging/.ui.conversationlist.ConversationListActivity t16}

    mResumedActivity: ActivityRecord{d0283af u0 com.android.messaging/.ui.conversationlist.ConversationListActivity t16}

  Stack #0:
  mFullscreen=true
  isSleeping=false
  mBounds=null
    Task id #12
    mFullscreen=true
    mBounds=null
    mMinWidth=-1
    mMinHeight=-1
    mLastNonFullscreenBounds=null
      TaskRecord{dcb575e #12 I=com.android.launcher3/.Launcher U=0 StackId=0 sz=1}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.launcher3/.Launcher }
        Hist #0: ActivityRecord{815643d u0 com.android.launcher3/.Launcher t12}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.launcher3/.Launcher }
          ProcessRecord{135de8b 3664:com.android.launcher3/u0a16}

    Running activities (most recent first):
      TaskRecord{dcb575e #12 I=com.android.launcher3/.Launcher U=0 StackId=0 sz=1}
        Run #0: ActivityRecord{815643d u0 com.android.launcher3/.Launcher t12}

  Stack #5:
  mFullscreen=true
  isSleeping=false
  mBounds=null
    Task id #15
    mFullscreen=true
    mBounds=null
    mMinWidth=-1
    mMinHeight=-1
    mLastNonFullscreenBounds=null
      TaskRecord{65933c4 #15 A=com.android.systemui U=0 StackId=5 sz=1}
      Intent { flg=0x10804000 cmp=com.android.systemui/.recents.RecentsActivity }
        Hist #0: ActivityRecord{445e887 u0 com.android.systemui/.recents.RecentsActivity t15}
          Intent { flg=0x10804000 cmp=com.android.systemui/.recents.RecentsActivity }
          ProcessRecord{ede67cc 1940:com.android.systemui/u0a25}

    Running activities (most recent first):
      TaskRecord{65933c4 #15 A=com.android.systemui U=0 StackId=5 sz=1}
        Run #0: ActivityRecord{445e887 u0 com.android.systemui/.recents.RecentsActivity t15}

ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)
Recent tasks:
* Recent #0: TaskRecord{2bef07f #16 A=com.android.messaging U=0 StackId=1 sz=1}
* Recent #1: TaskRecord{dcb575e #12 I=com.android.launcher3/.Launcher U=0 StackId=0 sz=1}
* Recent #2: TaskRecord{65933c4 #15 A=com.android.systemui U=0 StackId=5 sz=1}

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

​编辑
通过上图可以看到Stack #0,Stack #1就是ActivityStack,这些ActivityStack都属于,并不是我们既经常提到的任务栈,我们经常说的任务栈值的是TaskRecord,新创建一个APP打开的时候就会创建一个ActivityStack,同时也会创建TaskRecord,这个TaskRecord实例和启动activity的启动模式有关系,ActivityStack其实就是

AMS为了方便的管理这些Task而引入了ActivityStack,在ActivityStack内部通过ArrayList维护了一组TaskRecord。一般来说Launcher的Task属于单独的一个ActivityStack,称为Home Activity Stack,System UI如rencentActivity的Task属于一个单独ActivityStack,其他App的Task属于另一个ActivityStack,叫做mFocusedStack

 3.3 默认模式从DialtactsActivity启动ContactEditorActivity

Stack #1:
mFullscreen=true
isSleeping=false
mBounds=null
Task id #19
mFullscreen=true
mBounds=null
mMinWidth=-1
mMinHeight=-1
mLastNonFullscreenBounds=null
TaskRecord{661712c #19 A=com.android.dialer U=0 StackId=1 sz=2}
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 pkg=com.android.dialer cmp=com.android.dialer/.app.DialtactsActivity }
Hist #1: ActivityRecord{7b55e1 u0 com.android.contacts/.activities.ContactEditorActivity t19}
Intent { act=android.intent.action.INSERT dat=content://com.android.contacts/contacts cmp=com.android.contacts/.activities.ContactEditorActivity }
ProcessRecord{a39237e 6709:com.android.contacts/u0a6}
Hist #0: ActivityRecord{e698609 u0 com.android.dialer/.app.DialtactsActivity t19}
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 pkg=com.android.dialer cmp=com.android.dialer/.app.DialtactsActivity bnds=[48,2105][317,2385] }
ProcessRecord{44c2be7 6784:com.android.dialer/u0a13}

    Running activities (most recent first):
TaskRecord{661712c #19 A=com.android.dialer U=0 StackId=1 sz=2}
Run #1: ActivityRecord{7b55e1 u0 com.android.contacts/.activities.ContactEditorActivity t19}
Run #0: ActivityRecord{e698609 u0 com.android.dialer/.app.DialtactsActivity t19}

    mResumedActivity: ActivityRecord{7b55e1 u0 com.android.contacts/.activities.ContactEditorActivity t19}

​编辑

3.4 从A启动B创建新栈

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

 ​编辑

5. 启动模式

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

5.1 standerd

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

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

​编辑

5.2 singleTop

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

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

​编辑

如果在B上面以singleTop模式启动B的话,则不会重新创建B,只会调用onNewIntent()方法,其过程如图所示:​编辑

5.3 singleTask

如果要启动的Activity已经存在于它想要归属的栈中,那么不会创建该Activity实例,将栈中位于该Activity上的所有的Activity出栈,同时该Activity的onNewIntent()方法会被调用。
如果要启动的Activity不存在于它想要归属的栈中,并且该栈存在,则会创建该Activity的实例。
如果要启动的Activity想要归属的栈不存在,则首先要创建一个新栈,然后创建该Activity实例并压入到新栈中。

比如:现在有个A Activity,我们在A以standerd模式上面启动B,然后在B上面以singleTask模式启动A,其过程如图所示:​编辑

5.4 singleInstance

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

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

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默认具有此标记位的效果

四,结束语

鉴于作者水平有限,文章之中难免有错误或者遗漏地方,欢迎大家批评指正,也欢迎大家讨论,积极评论哈,谢谢

五.参考资料

www.jianshu.com/p/94816e52c…

juejin.cn/post/685629…