欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android四大组件之Activity启动流程源码实现详解

程序员文章站 2022-03-03 19:22:07
  Activity启动流程(三)- Activity Task调度算法复盘分析Android四大组件源码实现详解系列博客目录:Android应用进程创建流程大揭秘Android四大组件之bindService源码实现详解Android四大组件之Activity启动流程源码实现详解概要Android四大组件之Activity启动流程源码实现详解(一)Android四大组件之Activity启动流程源码实现详解(二)Activity启动流程(三)- Activity Task调度算法复盘分析....

Activity启动流程(三)- Activity Task调度算法复盘分析

Android四大组件源码实现详解系列博客目录:

Android应用进程创建流程大揭秘
Android四大组件之bindService源码实现详解
Android四大组件之Activity启动流程源码实现详解概要
Android四大组件之Activity启动流程源码实现详解(一)
Android四大组件之Activity启动流程源码实现详解(二)
Activity启动流程(三)- Activity Task调度算法复盘分析


前言

还记得我们在前面博客Android四大组件之Activity启动流程源码实现详解(二)中做的艰苦卓越的斗争吗!这场战役之惨烈,战况之持久前所未有!虽然过程是疼苦的,但是战果也是显赫和令人满意的,通过上述战役我们取得了如下的阶段性成果:

  • 初始化了Activity启动状态
  • 计算了启动launchFlag
  • 计算了调用者的ActivityStack
  • 检查了是否存在复用的TaskRecord
  • 对于存在复用的TaskRecord则进行相应的ActivityStack、TaskRecord的移动(说实话,我也没有真的搞懂,希望这块比较有经验的小伙们能和我一起学习)
  • 计算了当前启动Activity所属的TaskRecord
  • 把当前启动的Activity放到所属TaskRecord的栈顶
  • 并且前面的TaskRecord放到了ActivityStack的栈顶

总而言之经过上述一顿猛虎般的操作,此时要启动的目标Actvity及其对应的task位置以及ActivityStack已经安排妥当,现在可以准备接下来的相关工作了,本来我也是准备这么干的,可是总感觉分析少了点什么!通过前面的分析我们知道了ActivityStack类的startActivityUncheckedLocked方法负责调度ActivityRecord以及TaskRecord,并且通过前面的分析我们也可以知道调度算法非常复杂,最好需结合实际场景分析调度算法。但是前面我们只是对startActivityUncheckedLocked整个源码进行了流水式的分析,而没有结合实际,所以本篇博客将结合实际场景分析调度算法,将Activity启动过程中涉及的TaskRecord和ActivityStack的调度再来复盘一下!

注意:本篇的介绍是基于Android 7.xx平台为基础的,其中涉及的代码路径如下:

frameworks/base/services/core/java/com/android/server/am/
  --- ActivityManagerService.java
  --- ProcessRecord.java
  --- ActivityRecord.java
  --- ActivityResult.java
  --- ActivityStack.java
  --- ActivityStackSupervisor.java
  --- ActivityStarter.java
  --- TaskRecord.java 

frameworks/base/services/core/java/com/android/server/pm/
 --- PackageManagerService.java
 
frameworks/base/core/java/android/content/pm/
--- ActivityInfo.java

frameworks/base/core/java/android/app/
  --- IActivityManager.java
  --- ActivityManagerNative.java (内部包含AMP) --- ActivityManager.java
  --- AppGlobals.java
  --- Activity.java
  --- ActivityThread.java(内含AT) --- LoadedApk.java  
  --- AppGlobals.java
  --- Application.java
  --- Instrumentation.java
  
  --- IApplicationThread.java
  --- ApplicationThreadNative.java (内部包含ATP) --- ActivityThread.java (内含ApplicationThread) --- ContextImpl.java 

并且在后续的源码分析过程中为了简述方便,会将做如下简述:

  • ApplicationThreadProxy简称为ATP
  • ActivityManagerProxy简称为AMP
  • ActivityManagerService简称为AMS
  • ActivityManagerNative简称AMN
  • ApplicationThreadNative简称ATN
  • PackageManagerService简称为PKMS
  • ApplicationThread简称为AT
  • ActivityStarter简称为AS,这里不要和ActivityServices搞混淆了
  • ActivityStackSupervisor简称为ASS

在正式开始今天博客相关源码分析前,还是先奉上调用的时序图以便小伙们先从整体上有个清晰的概括,然后再从细节开撸!

Android四大组件之Activity启动流程源码实现详解



一.实际场景一复盘分析

假设我们存在如下的Activity启动场景某应用内有两个Activity,A和B,A为该应用入口Activity,从A可跳转至B,A和B的启动模式都为standard,我们以该场景为切入口来分析Activity启动过程中Task任务栈和Activity栈的调度。本章接将会重点围绕如下三种Activity启动情况分析:

  • 从Launcher桌面第一次启动应用时的任务调度情况:

  • 任务调度时会创建新task,并将新的ActivityRecord加入这个新的task,然后将task放入合适的Stack的栈顶

  • 应用内Activity跳转时的任务调度情况:

  • 任务调度时会将新的ActivityRecord加入已有的task,然后将该ActivityRecord移动到Task顶端,然后将task放入合适的Stack的栈顶

  • 然后按Home键,再打开应用程序时的调度情况:

  • 任务调度时会先找到已有的相关task,并显示栈顶的Activity

通过前面的博客Android四大组件之Activity启动流程源码实现详解(二)我们知道Activity启动过程中Task和ActivityStack的调度涉及的代码众多,所以在复盘的过程中我们只抽取重点,并将AS.startActivityUnchecked中的涉及的方法,我们将采用伪代码的方式进行分析,只为了小伙们们能更好的了解Task和ActivityStack的调度!


1.1 从Launcher桌面第一次启动应用时Activity的Task以及Stack调度情况

在正式启动前,我们先来看看此时Android终端中的各种Task和ActivityStack的情况,我们可以通过Android内置的命令dumpsys activity activities来进行查看,此时的Activity的栈以及Task情况如下:

#adb shell dumpsys activity activities
ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities) Display #0 (activities from top to bottom): Stack #0://Home所属Stack对应的taskid,其对应的值为HOME_STACK_ID 
  mFullscreen=true mBounds=null
    Task id #8 mFullscreen=true mBounds=null
    mMinWidth=-1 mMinHeight=-1 mLastNonFullscreenBounds=null * TaskRecord{3a6c4af #8 A=com.android.launcher3 U=0 StackId=0 sz=1} userId=0 effectiveUid=u0a23 mCallingUid=0 mUserSetupComplete=true mCallingPackage=null
      affinity=com.android.launcher3
      intent={act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.launcher3/.Launcher} realActivity=com.android.launcher3/.Launcher
      autoRemoveRecents=false isPersistable=true numFullscreen=1 taskType=1 mTaskToReturnTo=1 rootWasReset=false mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
      Activities=[ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8}] askedCompatMode=false inRecents=true isAvailable=true lastThumbnail=null lastThumbnailFile=/data/system_ce/0/recent_images/8_task_thumbnail.png
      stackId=0 hasBeenVisible=true mResizeMode=RESIZE_MODE_FORCE_RESIZEABLE isResizeable=false firstActiveTime=1602505452631 lastActiveTime=1602505452631 (inactive for 2s) * Hist #0: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8} packageName=com.android.launcher3 processName=com.android.launcher3
          launchedFromUid=0 launchedFromPackage=null userId=0 app=ProcessRecord{c4940bc 14811:com.android.launcher3/u0a23} Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.launcher3/.Launcher } frontOfTask=true task=TaskRecord{3a6c4af #8 A=com.android.launcher3 U=0 StackId=0 sz=1} taskAffinity=com.android.launcher3
          realActivity=com.android.launcher3/.Launcher
          baseDir=/system/app/PaxLauncher3/PaxLauncher3.apk
          dataDir=/data/user/0/com.android.launcher3
          stateNotNeeded=true componentSpecified=false mActivityType=1 compat={320dpi} labelRes=0x7f0a0001 icon=0x7f030001 theme=0x7f0d0002 config={1.0 ?mcc?mnc [zh_CN] ldltr sw360dp w360dp h568dp 320dpi nrml port finger -keyb/v/h -nav/h s.5} taskConfigOverride={1.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?} taskDescription: iconFilename=null label="null" color=ff222222
          launchFailed=false launchCount=0 lastLaunchTime=-1m17s726ms
          haveState=false icicle=null
          state=RESUMED stopped=false delayedResume=false finishing=false keysPaused=false inHistory=true visible=true sleeping=false idle=true mStartingWindowState=STARTING_WINDOW_NOT_SHOWN
          fullscreen=true noDisplay=false immersive=false launchMode=2 frozenBeforeDestroy=false forceNewConfig=false mActivityType=HOME_ACTIVITY_TYPE
          waitingVisible=false nowVisible=true lastVisibleTime=-2s190ms
          resizeMode=RESIZE_MODE_FORCE_RESIZEABLE

    Running activities (most recent first): TaskRecord{3a6c4af #8 A=com.android.launcher3 U=0 StackId=0 sz=1} Run #0: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8} mResumedActivity: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8} mLastPausedActivity: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8} mFocusedActivity: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8} mFocusedStack=ActivityStack{a520845 stackId=0, 1 tasks} mLastFocusedStack=ActivityStack{a520845 stackId=0, 1 tasks} mSleepTimeout=false mCurTaskIdForUser={0=8} mUserStackInFront={} mActivityContainers={0=ActivtyContainer{0}A} mLockTaskModeState=NONE mLockTaskPackages (userId:packages)= 0:[] mLockTaskModeTasks[] 

通过前面的dump,此时我们可以知道了ASS中的mHomeStack已经创建了,并且Launcher桌面Activity对应的Task和ActivityRecord都已经OK了!但是由于Launcher桌面Activity的启动不在本篇的分析中,它对应的Activity的Task以及相关的调度就不予分析了!

从Launcher桌面第一次启动应用时Activity的Task以及Stack调度执行如下所示:

//[ActivityStarter.java] /*
	这里的sourceRecord是指发起调用者 
	r是指本次的将要启动的Activity
	startFlags取值为0
	doResume的值为true
	inTask为发起方指定的任务栈,此时为null
	这里主要确定目标Activity的launchMode Task栈等,即Task的创建和管理
	*/ 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); /*****************************************************************************/ //这里小伙们就不要关注排版问题了,主要是为了演示整个流程,各位就将就一下 private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask, boolean doResume, int startFlags, ActivityRecord sourceRecord, IVoiceInteractionSession 
            							voiceSession, IVoiceInteractor voiceInteractor) { //此处分支会走,将启动过程中的涉及的相关变量进行初始化 reset(); ... mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP;//此时为false mLaunchSingleInstance = r.launchMode == LAUNCH_SINGLE_INSTANCE;//此时为false mLaunchSingleTask = r.launchMode == LAUNCH_SINGLE_TASK;//此时为false //这里重点关注一下,此时mIntent.getFlags携带的为FLAG_ACTIVITY_RESET_TASK_IF_NEEDED|FLAG_ACTIVITY_NEW_TASK,这个地方是关键因为后面会根据这些值进行Task和Stack的调度 mLaunchFlags = adjustLaunchFlagsToDocumentMode(r,mLaunchSingleInstance, mLaunchSingleTask, mIntent.getFlags()); ... mDoResume = doResume; ... mInTask = inTask; ... mStartFlags = startFlags;//此时的mStartFlags为0 } /*****************************************************************************/ //根据发起端,计算目标Activity的launchMode模式 computeLaunchingTaskFlags(); /*****************************************************************************/ //这里小伙们就不要关注排版问题了,主要是为了演示整个流程,各位就将就一下 //根据发起者launchMode情况,决定目标Activity的Flags情况 private void computeLaunchingTaskFlags() { //此时场景下的mSourceRecord不为null,mInTask为null,所以不会进入该分支 if (mSourceRecord == null && mInTask != null && mInTask.stack != null) { ... }else {//会进入此分支 mInTask = null; //不满足条件,不会进入该分支 if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null && mSourceRecord.isFreeform()) { ... } } } //此时的mInTask为null,会进入此分支,但是该分支下面的三个小分支都不会进入 if (mInTask == null) { if (mSourceRecord == null) {//mSourceRecord不为null,此时的mSourceRecord为Launcher,不会进入此分支 ... } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {//launcher应用activity的启动模式为singleTask ... } else if (mLaunchSingleInstance || mLaunchSingleTask) {//不会进入此分支,此时都为false ... } } /*****************************************************************************/ //确定发起端的ActivityStack情况 computeSourceStack(); /*****************************************************************************/ //这里小伙们就不要关注排版问题了,主要是为了演示整个流程,各位就将就一下  //确定发起端的Stack情况 private void computeSourceStack() { if (mSourceRecord == null) {//mSourceRecord不为null,不会进入此分支 ... } if (!mSourceRecord.finishing) {//此时明显mSourceRecord没有被finish所以会进入此分支 //当调用者Activity不为空,且不处于finishing状态,则其所在栈赋于sourceStack mSourceStack = mSourceRecord.task.stack; return; } ... } /*****************************************************************************/ mIntent.setFlags(mLaunchFlags);//设置目标Activity的launchMode启动模式 // 根据mLaunchFlags来查找是否有可复用的activity /**
	      * 这边主要是判断当前启动的Activity是否存在可以利用的Task
	      * 当启动模式launchMode为singleTask、singleInstance,或者启动时
	      * Flag设置为FLAG_ACTIVITY_NEW_TASK并没设置FLAG_ACTIVITY_MULTIPLE_TASK
	      * 并且当前启动的Activity不是以startActivityForResult启动的,
	      * 满足以上情况才会寻找是否存在有复用的Task。
	      * 匹配规则:
	      * 1、对于启动模式为singleInstance,遍历所有ActivityStack和Task的堆栈中查找
	      *是否存在以当前启动Activity相同的Activity。
	      * 2、其它情况下,遍历所有ActivityStack和Task的堆栈,查找Task中intent变量                                                      * 是否当前启动Activity相匹配,如果不存在,则去匹配task的亲和性(即
	  	   *在AndroidManifest中android:taskAffinity定义的。
	      */ //此时肯定不存在复用的Activity,因为除开启动了Launcher对应的Activity,啥都还没有启动呢 mReusedActivity = getReusableIntentActivity(); /*****************************************************************************/ //这里小伙们就不要关注排版问题了,主要是为了演示整个流程,各位就将就一下  private ActivityRecord getReusableIntentActivity() { //此时的mLaunchFlags的取值为FLAG_ACTIVITY_RESET_TASK_IF_NEEDED|FLAG_ACTIVITY_NEW_TASK,所以为true boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 && (mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0) || mLaunchSingleInstance || mLaunchSingleTask; //此时mInTask为false,mStartActivity.resultTo为null putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null; ActivityRecord intentActivity = null; if (mOptions != null && mOptions.getLaunchTaskId() != -1) { ... } else if (putIntoExistingTask) { if (mLaunchSingleInstance) {//不会进入此分支 ... } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {//不会进入此分支 ... } else {//会走入此分支,在ASS中查找是否存在启动目标Activity,很明显此时不存在,所以intentActivity得到的值为null intentActivity = mSupervisor.findTaskLocked(mStartActivity); } } return intentActivity; } /*****************************************************************************/ ... //如果找到了可重用的activity,则进行下一步相关操作,在此场景下很明显没有找到可复用的Activity if (mReusedActivity != null) {//不会进入此分支,忽略 ... } if (mStartActivity.packageName == null) {//异常处理,正常启动不会进入此分支忽略 ... } //是否需要启动新的Activity标记,此场景下dontStart为false final boolean dontStart = top != null && mStartActivity.resultTo == null && top.realActivity.equals(mStartActivity.realActivity) && top.userId == mStartActivity.userId && top.app != null && top.app.thread != null && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop || mLaunchSingleTask); if (dontStart) {//不会走入此分支,忽略 ... } //表示是否需要创建新的任务栈 boolean newTask = false; ... /*
        	如果要启动的目标Activity没有对应的resultTo,很明显由于mLaunchFlags携带FLAG_ACTIVITY_NEW_TASK所以result会被置为null
        	并且也没有添加到对应栈中,mAddingToTask为false
        	而且设置了FLAG_ACTIVITY_NEW_TASK。
        	说明没有找到对应的栈来启动我们的Activity。
            所以会通过创建或者复用一个栈来存放Activity
            此场景下会进入该分支
        */ if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {//此场景会进入此分支 newTask = true; // 重用启动端Activity所属Task或者新建task setTaskFromReuseOrCreateNewTask(taskToAffiliate); /*****************************************************************************/ //这里小伙们就不要关注排版问题了,主要是为了演示整个流程,各位就将就一下  //从发起端获取Task任务栈或者新建一个任务栈 //此处的入参taskToAffiliate为null private void setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate) { //获取目标ActivityStack栈,即目标Activity所属的Stack栈 mTargetStack = computeStackFocus(mStartActivity, true, mLaunchBounds, mLaunchFlags, mOptions); if (mReuseTask == null) {//此时mReuseTask为null,会进入该分支 //创建新的Task final TaskRecord task = mTargetStack.createTaskRecord( mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info, mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession, mVoiceInteractor, !mLaunchTaskBehind /* toTop */); //将创建的Task设置为目标Activity的Task mStartActivity.setTask(task, taskToAffiliate); if (mLaunchBounds != null) {//此时的mLaunchBounds为null,不会进入此分支 ... } } else {//不会进入此分支,因为此时的mReuseTask为null,在setInitialState被设置的,后续没有被修改过 mStartActivity.setTask(mReuseTask, taskToAffiliate); } } /*****************************************************************************/ ... if (!mMovedOtherTask) {//会走入此分支 updateTaskReturnToType(mStartActivity.task, mLaunchFlags, topStack); } } /*
			当mSourceRecord不为空,把新的ActivityRecord绑定到启动者的TaskRecord上。
         	一般情况下,mSourceRecord就是调用者,如本例中的Launcher;
        	但也有特殊情况,举个例子,如果启动模式为singleTask,栈中又不存在相同的Activity时,
        	mSourceRecord就是栈顶的Activity
		*/ else if (mSourceRecord != null) {//不会进入此分支 ... } else if (mInTask != null) {//启动时指定了目标栈(mInTask),ActivityRecord绑定到mInTask,不会进入此分支 ... } else {//不会进入此分支 ... } //权限检测 mService.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName, mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId); if (mSourceRecord != null && mSourceRecord.isRecentsActivity()) {//不会走入此分支,因为我们的发起端是Luancher不属于RecentsActivity ... } ... /*把当前启动的Activity加入TaskRecord以及绑定WindowManagerService*/ mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions); /*****************************************************************************/ //这里小伙们就不要关注排版问题了,主要是为了演示整个流程,各位就将就一下  final void startActivityLocked( ActivityRecord r, //此时的r为目标Activity boolean newTask, //newTask表示是否要创建Task,为true boolean keepCurTransition, ActivityOptions options) { TaskRecord rTask = r.task; final int taskId = rTask.taskId; if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {//会进入此分支 //task中的上一个activity已被移除,或者ams重用该task,则将该task移到顶部 insertTaskAtTop(rTask, r);//将前面创建的Task放到Stack的顶部 mWindowManager.moveTaskToTop(taskId); } TaskRecord task = null; if (!newTask) {//newTask为true不会走此分支 ... } ... task = r.task; //将Activity移动到Stack的顶端 task.addActivityToTop(r); task.setFrontOfTask(); r.putInHistory(); if (!isHomeStack() || numActivities() > 0) {//会进入此分支,此时的ActivityStack不是HomeStack //这个地方很重要 addConfigOverride(r, task); } else {//不会进入此分支 ... } ... } /*****************************************************************************/ ... } 

至此从Launcher桌面第一次启动应用时Activity的Task以及Stack调度就完成了,我们对其小结一下,其主要流程可以精简为如下几个步骤:

  • 调用AS.setInitialState方法,对启动目标Activity时Task任务栈和ActivityStack栈将要涉及的变量进行初始化设置
  • 根据发起端Activity的情况,调用computeLaunchingTaskFlags计算目标Activity的launchMode模式
  • 调用computeSourceStack确定发起端Stack的情况
  • 经过上述的一系列处理以后,已经确定目标Activity相关的LauncherMode,flag等启动模式了,接下来调用getReusableIntentActivity确定是否有可复用的Activity,在此场景下肯定不会存在可以复用的Activity
  • 接着调用方法setTaskFromReuseOrCreateNewTask创建目标Activity对应的Task和找到合适的Stack,并且将创建的Task移动到目标Stack的顶端
  • 接着继续调用ASS.startActivityLocked方法,将目标Activity加入TaskRecord,并且将其放入对应ActivityStack的顶部以及绑定WindowManagerService

经过如上的步骤以后,我们的目标Activity所属的Task和Stack就已经安排妥当了,我们此时可以通过命令查看,可以看到此时的mFocusedActivity和mFocusedStack为目标Activity。

#adb shell dumpsys activity activities
ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities) Display #0 (activities from top to bottom): Stack #1://目标Activity对应的Stackid,其值为FULLSCREEN_WORKSPACE_STACK_ID的值
  mFullscreen=true mBounds=null
    Task id #9 mFullscreen=true mBounds=null
    mMinWidth=-1 mMinHeight=-1 mLastNonFullscreenBounds=null * TaskRecord{d361623 #9 A=com.example.test U=0 StackId=1 sz=1} userId=0 effectiveUid=u0a48 mCallingUid=u0a23 mUserSetupComplete=true mCallingPackage=com.android.launcher3
      affinity=com.example.test
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.example.test/.MainActivity} realActivity=com.example.test/.MainActivity
      autoRemoveRecents=false isPersistable=true numFullscreen=1 taskType=0 mTaskToReturnTo=1 rootWasReset=true mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
      Activities=[ActivityRecord{dd0bb9a u0 com.example.test/.AMainActivity t9}] askedCompatMode=false inRecents=true isAvailable=true lastThumbnail=null lastThumbnailFile=/data/system_ce/0/recent_images/9_task_thumbnail.png
      stackId=1 hasBeenVisible=true mResizeMode=RESIZE_MODE_FORCE_RESIZEABLE isResizeable=true firstActiveTime=1602505456984 lastActiveTime=1602505456984 (inactive for 3s) * Hist #0: ActivityRecord{dd0bb9a u0 com.example.test/.AMainActivity t9} packageName=com.example.test processName=com.example.test
          launchedFromUid=10023 launchedFromPackage=com.android.launcher3 userId=0 app=ProcessRecord{cd84720 15340:com.example.test/u0a48} Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.example.test/.AMainActivity bnds=[184,356][360,544] } frontOfTask=true task=TaskRecord{d361623 #9 A=com.example.test U=0 StackId=1 sz=1} taskAffinity=com.example.test
          realActivity=com.example.test/.MainActivity
          baseDir=/data/app/com.example.test-1/base.apk
          dataDir=/data/user/0/com.example.test
          stateNotNeeded=false componentSpecified=true mActivityType=0 compat={320dpi} labelRes=0x7f050000 icon=0x7f020000 theme=0x7f060001 config={1.0 ?mcc?mnc [zh_CN] ldltr sw360dp w360dp h568dp 320dpi nrml port finger -keyb/v/h -nav/h s.5} taskConfigOverride={1.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?} taskDescription: iconFilename=null label="null" color=ffe6e6e6
          launchFailed=false launchCount=1 lastLaunchTime=-3s640ms
          haveState=false icicle=null
          state=RESUMED stopped=false delayedResume=false finishing=false keysPaused=false inHistory=true visible=true sleeping=false idle=true mStartingWindowState=STARTING_WINDOW_SHOWN
          fullscreen=true noDisplay=false immersive=false launchMode=0 frozenBeforeDestroy=false forceNewConfig=false mActivityType=APPLICATION_ACTIVITY_TYPE
          waitingVisible=false nowVisible=true lastVisibleTime=-3s152ms
          resizeMode=RESIZE_MODE_FORCE_RESIZEABLE

    Running activities (most recent first): TaskRecord{d361623 #9 A=com.example.test U=0 StackId=1 sz=1} Run #0: ActivityRecord{dd0bb9a u0 com.example.test/.AMainActivity t9} mResumedActivity: ActivityRecord{dd0bb9a u0 com.example.test/.AMainActivity t9} Stack #0: mFullscreen=true mBounds=null
    Task id #8 mFullscreen=true mBounds=null
    mMinWidth=-1 mMinHeight=-1 mLastNonFullscreenBounds=null * TaskRecord{3a6c4af #8 A=com.android.launcher3 U=0 StackId=0 sz=1} userId=0 effectiveUid=u0a23 mCallingUid=0 mUserSetupComplete=true mCallingPackage=null
      affinity=com.android.launcher3
      intent={act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.launcher3/.Launcher} realActivity=com.android.launcher3/.Launcher
      autoRemoveRecents=false isPersistable=true numFullscreen=1 taskType=1 mTaskToReturnTo=1 rootWasReset=false mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
      Activities=[ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8}] askedCompatMode=false inRecents=true isAvailable=true lastThumbnail=null lastThumbnailFile=/data/system_ce/0/recent_images/8_task_thumbnail.png
      stackId=0 hasBeenVisible=true mResizeMode=RESIZE_MODE_FORCE_RESIZEABLE isResizeable=false firstActiveTime=1602505456850 lastActiveTime=1602505456850 (inactive for 3s) * Hist #0: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8} packageName=com.android.launcher3 processName=com.android.launcher3
          launchedFromUid=0 launchedFromPackage=null userId=0 app=ProcessRecord{c4940bc 14811:com.android.launcher3/u0a23} Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.launcher3/.Launcher } frontOfTask=true task=TaskRecord{3a6c4af #8 A=com.android.launcher3 U=0 StackId=0 sz=1} taskAffinity=com.android.launcher3
          realActivity=com.android.launcher3/.Launcher
          baseDir=/system/app/PaxLauncher3/PaxLauncher3.apk
          dataDir=/data/user/0/com.android.launcher3
          stateNotNeeded=true componentSpecified=false mActivityType=1 compat={320dpi} labelRes=0x7f0a0001 icon=0x7f030001 theme=0x7f0d0002 config={1.0 ?mcc?mnc [zh_CN] ldltr sw360dp w360dp h568dp 320dpi nrml port finger -keyb/v/h -nav/h s.5} taskConfigOverride={1.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?} taskDescription: iconFilename=null label="null" color=ff222222
          launchFailed=false launchCount=0 lastLaunchTime=-1m23s381ms
          haveState=true icicle=Bundle[mParcelledData.dataSize=3788] state=STOPPED stopped=true delayedResume=false finishing=false keysPaused=false inHistory=true visible=false sleeping=false idle=true mStartingWindowState=STARTING_WINDOW_NOT_SHOWN
          fullscreen=true noDisplay=false immersive=false launchMode=2 frozenBeforeDestroy=false forceNewConfig=false mActivityType=HOME_ACTIVITY_TYPE
          waitingVisible=false nowVisible=false lastVisibleTime=-7s845ms
          resizeMode=RESIZE_MODE_FORCE_RESIZEABLE

    Running activities (most recent first): TaskRecord{3a6c4af #8 A=com.android.launcher3 U=0 StackId=0 sz=1} Run #0: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8} mLastPausedActivity: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8} mFocusedActivity: ActivityRecord{dd0bb9a u0 com.example.test/.AMainActivity t9} mFocusedStack=ActivityStack{8238d9 stackId=1, 1 tasks} mLastFocusedStack=ActivityStack{8238d9 stackId=1, 1 tasks} mSleepTimeout=false mCurTaskIdForUser={0=9} mUserStackInFront={} mActivityContainers={0=ActivtyContainer{0}A, 1=ActivtyContainer{1}A} mLockTaskModeState=NONE mLockTaskPackages (userId:packages)= 0:[] mLockTaskModeTasks[] 

1.2 从已经启动应用的A Activity跳转到B Activity

通过这种方式启动目标Activity较前面的启动方式要简单一些,因为无需创建新的Task了,只需要将目标Activity加入到A所属的Task即可,注意此时启动B Activity是标准的启动方式。此时启动目标B Activity的Task以及Stack调度执行如下所示:

//[ActivityStarter.java] /*
	这里的sourceRecord是指发起调用者 
	r是指本次的将要启动的Activity
	startFlags取值为0
	doResume的值为true
	inTask为发起方指定的任务栈,此时为null
	这里主要确定目标Activity的launchMode Task栈等,即Task的创建和管理
	*/ 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); /*****************************************************************************/ //这里小伙们就不要关注排版问题了,主要是为了演示整个流程,各位就将就一下 private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask, boolean doResume, int startFlags, ActivityRecord sourceRecord, IVoiceInteractionSession 
            							voiceSession, IVoiceInteractor voiceInteractor) { //此处分支会走,将启动过程中的涉及的相关变量进行初始化 reset(); ... mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP;//此时为false mLaunchSingleInstance = r.launchMode == LAUNCH_SINGLE_INSTANCE;//此时为false mLaunchSingleTask = r.launchMode == LAUNCH_SINGLE_TASK;//此时为false //这里重点关注一下,此时mIntent.getFlags携带的flags为0,此处很关键 mLaunchFlags = adjustLaunchFlagsToDocumentMode(r,mLaunchSingleInstance, mLaunchSingleTask, mIntent.getFlags()); ... mDoResume = doResume; ... mInTask = inTask; ... mStartFlags = startFlags;//此时的mStartFlags为0 } /*****************************************************************************/ //根据发起端,计算目标Activity的launchMode模式 computeLaunchingTaskFlags(); /*****************************************************************************/ //这里小伙们就不要关注排版问题了,主要是为了演示整个流程,各位就将就一下 //根据发起者launchMode情况,决定目标Activity的Flags情况 private void computeLaunchingTaskFlags() { //此时场景下的mSourceRecord不为null,mInTask为null,所以不会进入该分支 if (mSourceRecord == null && mInTask != null && mInTask.stack != null) { ... }else {//会进入此分支 mInTask = null; //不满足条件,不会进入该分支 if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null && mSourceRecord.isFreeform()) { mAddingToTask = true; } } } //此时的mInTask为null,会进入此分支,但是该分支下面的三个小分支都不会进入 if (mInTask == null) { if (mSourceRecord == null) {//mSourceRecord不为null,此时的mSourceRecord为Launcher,不会进入此分支 ... } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {//launcher应用activity的启动模式为singleTask ... } else if (mLaunchSingleInstance || mLaunchSingleTask) {//不会进入此分支,此时都为false ... } } /*****************************************************************************/ //确定发起端的ActivityStack情况 computeSourceStack(); /*****************************************************************************/ //这里小伙们就不要关注排版问题了,主要是为了演示整个流程,各位就将就一下  //确定发起端的Stack情况 private void computeSourceStack() { if (mSourceRecord == null) {//mSourceRecord不为null,不会进入此分支 ... } if (!mSourceRecord.finishing) {//此时明显mSourceRecord没有被finish所以会进入此分支 //当调用者Activity不为空,且不处于finishing状态,则其所在栈赋于sourceStack mSourceStack = mSourceRecord.task.stack; return; } ... } /*****************************************************************************/ mIntent.setFlags(mLaunchFlags);//设置目标Activity的launchMode启动模式 // 根据mLaunchFlags来查找是否有可复用的activity /**
	      * 这边主要是判断当前启动的Activity是否存在可以利用的Task
	      * 当启动模式launchMode为singleTask、singleInstance,或者启动时
	      * Flag设置为FLAG_ACTIVITY_NEW_TASK并没设置FLAG_ACTIVITY_MULTIPLE_TASK
	      * 并且当前启动的Activity不是以startActivityForResult启动的,
	      * 满足以上情况才会寻找是否存在有复用的Task。
	      * 匹配规则:
	      * 1、对于启动模式为singleInstance,遍历所有ActivityStack和Task的堆栈中查找
	      *是否存在以当前启动Activity相同的Activity。
	      * 2、其它情况下,遍历所有ActivityStack和Task的堆栈,查找Task中intent变量                                                      * 是否当前启动Activity相匹配,如果不存在,则去匹配task的亲和性(即
	  	   *在AndroidManifest中android:taskAffinity定义的。
	      */ //此时肯定不存在复用的Activity,因为除开启动了A对应的Activity,啥都还没有启动呢 mReusedActivity = getReusableIntentActivity(); /*****************************************************************************/ //这里小伙们就不要关注排版问题了,主要是为了演示整个流程,各位就将就一下  private ActivityRecord getReusableIntentActivity() { //此时的mLaunchFlags为0,而mLaunchSingleInstance为false,mLaunchSingleTask也为false,肯定不会存在可以复用的Activity boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 && (mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0) || mLaunchSingleInstance || mLaunchSingleTask; putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null; ActivityRecord intentActivity = null; if (mOptions != null && mOptions.getLaunchTaskId() != -1) { ... } else if (putIntoExistingTask) { ... } return intentActivity; } /*****************************************************************************/ //如果找到了可重用的activity,则进行下一步相关操作,在此场景下很明显没有找到可复用的Activity if (mReusedActivity != null) {//不会进入此分支,忽略 ... } if (mStartActivity.packageName == null) {//异常处理,正常启动不会进入此分支忽略 ... } //是否需要启动新的Activity标记,此场景下dontStart为false final boolean dontStart = top != null && mStartActivity.resultTo == null && top.realActivity.equals(mStartActivity.realActivity) && top.userId == mStartActivity.userId && top.app != null && top.app.thread != null && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop || mLaunchSingleTask); if (dontStart) {//不会走入此分支,忽略 ... } //表示是否需要创建新的任务栈 boolean newTask = false; ... /*
        	如果要启动的目标Activity没有对应的resultTo,
        	并且也没有添加到对应栈中
        	而且设置了FLAG_ACTIVITY_NEW_TASK。
        	说明没有找到对应的栈来启动我们的Activity。
            所以会通过创建或者复用一个栈来存放Activity
        */ //不会进入此分支 if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) { ... ... } /*
			当mSourceRecord不为空,把新的ActivityRecord绑定到启动者的TaskRecord上。
			此场景下会走入此分支,从发起端Activity A获取Task以及它所属的Stack
		*/ else if (mSourceRecord != null) { // 不是新建task的,重用原activity的task final int result = setTaskFromSourceRecord(); /*****************************************************************************/ //这里小伙们就不要关注排版问题了,主要是为了演示整个流程,各位就将就一下  private int setTaskFromSourceRecord() { //获取启动Activity的任务栈 final TaskRecord sourceTask = mSourceRecord.task; //此时的发起端Actiivty所在的TaskRecord就是处于sourceStack栈顶,所以sourceStack.topTask就是要启动的Activity所在的栈 //如果目标Activity不允许在屏幕上显示或者源任务栈和目标任务不在同一个栈 final boolean moveStackAllowed = sourceTask.stack.topTask() != sourceTask; //获取当前要启动activity所属的ActivityStack栈 if (moveStackAllowed) {//不会进入此分支  ... } //目标ActivityStack为空 if (mTargetStack == null) { mTargetStack = sourceTask.stack;//进入此分支 } else if (mTargetStack != sourceTask.stack) { //把启动方的任务栈绑定到目标ActivityStack上 ... } if (mDoResume) { mTargetStack.moveToFront("sourceStackToFront"); } //获取目标ActivityStack的顶部task final TaskRecord topTask = mTargetStack.topTask(); if (topTask != sourceTask && !mAvoidMoveToFront) {//不会走入此分支 } //如果目标activity还没有加入到栈中,而且启动标志设置了CLEAR_TOP,那么我们将Activity添加到已经存在的任务栈中,并调用clear方法清空对应的activity if (!mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0) {//很明显不会进入此分支 ... } else if (!mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {//不会进入此分支 ... } mStartActivity.setTask(sourceTask, null);//设置目标Activity B的Task为A Activity所属的Task return START_SUCCESS; } /*****************************************************************************/ if (result != START_SUCCESS) { return result; } } else if (mInTask != null) {//启动时指定了目标栈(mInTask),ActivityRecord绑定到mInTask,此场景下不会进入此分支 ... } else {//不会进入此分支,忽略 .., } ... /*把当前启动的Activity加入TaskRecord以及绑定WindowManagerService*/ mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions); /*****************************************************************************/ //这里小伙们就不要关注排版问题了,主要是为了演示整个流程,各位就将就一下  final void startActivityLocked( ActivityRecord r, //此时的r为目标Activity boolean newTask, //newTask表示是否要创建Task,为true boolean keepCurTransition, ActivityOptions options) { TaskRecord rTask = r.task; final int taskId = rTask.taskId; if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {//不会进入此分支 ... } TaskRecord task = null; if (!newTask) {//newTask为false会走入此分支 ... } ... task = r.task; //将Activity移动到Stack的顶端 task.addActivityToTop(r); task.setFrontOfTask(); r.putInHistory(); if (!isHomeStack() || numActivities() > 0) {//会进入此分支,此时的ActivityStack不是HomeStack //这个地方很重要 addConfigOverride(r, task); } else {//不会进入此分支 ... } ... } /*****************************************************************************/ ... } 

至此从从已经启动应用的A Activity跳转到B Activity的Task以及Stack的调度就完成了,我们对其小结一下,其主要流程可以精简为如下几个步骤:

  • 调用AS.setInitialState方法,对启动目标Activity时Task任务栈和ActivityStack栈将要涉及的变量进行初始化设置
  • 根据发起端Activity的情况,调用computeLaunchingTaskFlags计算目标Activity的launchMode模式
  • 调用computeSourceStack确定发起端Stack的情况
  • 经过上述的一系列处理以后,已经确定目标Activity相关的LauncherMode,flag等启动模式了,接下来调用getReusableIntentActivity确定是否有可复用的Activity,在此场景下肯定不会存在可以复用的Activity
  • 接着调用方法setTaskFromSourceRecord从发起端Activity获取Task和Stack
  • 接着继续调用ASS.startActivityLocked方法,将目标Activity加入TaskRecord,并且将其放入对应ActivityStack的顶部以及绑定WindowManagerService

经过如上的步骤以后,我们的目标Activity所属的Task和Stack就已经安排妥当了,我们此时可以通过命令查看,可以看到此时的mFocusedStack和Task为目标Activity A所对应的。

#adb shell dumpsys activity activities
ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities) Display #0 (activities from top to bottom): Stack #1://目标Activity对应的Stackid,其值为FULLSCREEN_WORKSPACE_STACK_ID的值
  mFullscreen=true mBounds=null
    Task id #9 mFullscreen=true mBounds=null
    mMinWidth=-1 mMinHeight=-1 mLastNonFullscreenBounds=null * TaskRecord{d361623 #9 A=com.example.test U=0 StackId=1 sz=2} userId=0 effectiveUid=u0a48 mCallingUid=u0a23 mUserSetupComplete=true mCallingPackage=com.android.launcher3
      affinity=com.example.test
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.example.test/.MainActivity} realActivity=com.example.test/.MainActivity
      autoRemoveRecents=false isPersistable=true numFullscreen=2 taskType=0 mTaskToReturnTo=1 rootWasReset=true mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
      Activities=[ActivityRecord{dd0bb9a u0 com.example.test/.AMainActivity t9}, ActivityRecord{59a829e u0 com.example.test/.BActivity t9}] askedCompatMode=false inRecents=true isAvailable=true lastThumbnail=null lastThumbnailFile=/data/system_ce/0/recent_images/9_task_thumbnail.png
      stackId=1 hasBeenVisible=true mResizeMode=RESIZE_MODE_FORCE_RESIZEABLE isResizeable=true firstActiveTime=1602505463616 lastActiveTime=1602505463616 (inactive for 2s) * Hist #1: ActivityRecord{59a829e u0 com.example.test/.BActivity t9}//任务栈顶Activity为BActivity packageName=com.example.test processName=com.example.test
          launchedFromUid=10048 launchedFromPackage=com.example.test userId=0 app=ProcessRecord{cd84720 15340:com.example.test/u0a48} Intent { cmp=com.example.test/.BActivity } frontOfTask=false task=TaskRecord{d361623 #9 A=com.example.test U=0 StackId=1 sz=2} taskAffinity=com.example.test
          realActivity=com.example.test/.BActivity
          baseDir=/data/app/com.example.test-1/base.apk
          dataDir=/data/user/0/com.example.test
          stateNotNeeded=false componentSpecified=true mActivityType=0 compat={320dpi} labelRes=0x7f050000 icon=0x7f020000 theme=0x7f060001 config={1.0 ?mcc?mnc [zh_CN] ldltr sw360dp w360dp h568dp 320dpi nrml port finger -keyb/v/h -nav/h s.5} taskConfigOverride={1.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?} taskDescription: iconFilename=null label="null" color=ffe6e6e6
          launchFailed=false launchCount=1 lastLaunchTime=-2s860ms
          haveState=false icicle=null
          state=RESUMED stopped=false delayedResume=false finishing=false keysPaused=false inHistory=true visible=true sleeping=false idle=true mStartingWindowState=STARTING_WINDOW_NOT_SHOWN
          fullscreen=true noDisplay=false immersive=false launchMode=0 frozenBeforeDestroy=false forceNewConfig=false mActivityType=APPLICATION_ACTIVITY_TYPE
          waitingVisible=false nowVisible=true lastVisibleTime=-2s362ms
          resizeMode=RESIZE_MODE_FORCE_RESIZEABLE * Hist #0: ActivityRecord{dd0bb9a u0 com.example.test/.AMainActivity t9} packageName=com.example.test processName=com.example.test
          launchedFromUid=10023 launchedFromPackage=com.android.launcher3 userId=0 app=ProcessRecord{cd84720 15340:com.example.test/u0a48} Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.example.test/.AMainActivity bnds=[184,356][360,544] } frontOfTask=true task=TaskRecord{d361623 #9 A=com.example.test U=0 StackId=1 sz=2} taskAffinity=com.example.test
          realActivity=com.example.test/.MainActivity
          baseDir=/data/app/com.example.test-1/base.apk
          dataDir=/data/user/0/com.example.test
          stateNotNeeded=false componentSpecified=true mActivityType=0 compat={320dpi} labelRes=0x7f050000 icon=0x7f020000 theme=0x7f060001 config={1.0 ?mcc?mnc [zh_CN] ldltr sw360dp w360dp h568dp 320dpi nrml port finger -keyb/v/h -nav/h s.5} taskConfigOverride={1.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?} taskDescription: iconFilename=null label="null" color=ffe6e6e6
          launchFailed=false launchCount=0 lastLaunchTime=-9s493ms
          haveState=true icicle=Bundle[mParcelledData.dataSize=1132] state=STOPPED stopped=true delayedResume=false finishing=false keysPaused=false inHistory=true visible=false sleeping=false idle=true mStartingWindowState=STARTING_WINDOW_SHOWN
          fullscreen=true noDisplay=false immersive=false launchMode=0 frozenBeforeDestroy=false forceNewConfig=false mActivityType=APPLICATION_ACTIVITY_TYPE
          waitingVisible=false nowVisible=false lastVisibleTime=-9s5ms
          resizeMode=RESIZE_MODE_FORCE_RESIZEABLE

    Running activities (most recent first): TaskRecord{d361623 #9 A=com.example.test U=0 StackId=1 sz=2} Run #1: ActivityRecord{59a829e u0 com.example.test/.BActivity t9} Run #0: ActivityRecord{dd0bb9a u0 com.example.test/.AMainActivity t9} mResumedActivity: ActivityRecord{59a829e u0 com.example.test/.BActivity t9} mLastPausedActivity: ActivityRecord{dd0bb9a u0 com.example.test/.AMainActivity t9} Stack #0: mFullscreen=true mBounds=null
    Task id #8 mFullscreen=true mBounds=null
    mMinWidth=-1 mMinHeight=-1 mLastNonFullscreenBounds=null * TaskRecord{3a6c4af #8 A=com.android.launcher3 U=0 StackId=0 sz=1} userId=0 effectiveUid=u0a23 mCallingUid=0 mUserSetupComplete=true mCallingPackage=null
      affinity=com.android.launcher3
      intent={act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.launcher3/.Launcher} realActivity=com.android.launcher3/.Launcher
      autoRemoveRecents=false isPersistable=true numFullscreen=1 taskType=1 mTaskToReturnTo=1 rootWasReset=false mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
      Activities=[ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8}] askedCompatMode=false inRecents=true isAvailable=true lastThumbnail=null lastThumbnailFile=/data/system_ce/0/recent_images/8_task_thumbnail.png
      stackId=0 hasBeenVisible=true mResizeMode=RESIZE_MODE_FORCE_RESIZEABLE isResizeable=false firstActiveTime=1602505456850 lastActiveTime=1602505456850 (inactive for 9s) * Hist #0: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8} packageName=com.android.launcher3 processName=com.android.launcher3
          launchedFromUid=0 launchedFromPackage=null userId=0 app=ProcessRecord{c4940bc 14811:com.android.launcher3/u0a23} Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.launcher3/.Launcher } frontOfTask=true task=TaskRecord{3a6c4af #8 A=com.android.launcher3 U=0 StackId=0 sz=1} taskAffinity=com.android.launcher3
          realActivity=com.android.launcher3/.Launcher
          baseDir=/system/app/PaxLauncher3/PaxLauncher3.apk
          dataDir=/data/user/0/com.android.launcher3
          stateNotNeeded=true componentSpecified=false mActivityType=1 compat={320dpi} labelRes=0x7f0a0001 icon=0x7f030001 theme=0x7f0d0002 config={1.0 ?mcc?mnc [zh_CN] ldltr sw360dp w360dp h568dp 320dpi nrml port finger -keyb/v/h -nav/h s.5} taskConfigOverride={1.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?} taskDescription: iconFilename=null label="null" color=ff222222
          launchFailed=false launchCount=0 lastLaunchTime=-1m29s234ms
          haveState=true icicle=Bundle[mParcelledData.dataSize=3788] state=STOPPED stopped=true delayedResume=false finishing=false keysPaused=false inHistory=true visible=false sleeping=false idle=true mStartingWindowState=STARTING_WINDOW_NOT_SHOWN
          fullscreen=true noDisplay=false immersive=false launchMode=2 frozenBeforeDestroy=false forceNewConfig=false mActivityType=HOME_ACTIVITY_TYPE
          waitingVisible=false nowVisible=false lastVisibleTime=-13s698ms
          resizeMode=RESIZE_MODE_FORCE_RESIZEABLE

    Running activities (most recent first): TaskRecord{3a6c4af #8 A=com.android.launcher3 U=0 StackId=0 sz=1} Run #0: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8} mLastPausedActivity: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8} mFocusedActivity: ActivityRecord{59a829e u0 com.example.test/.BActivity t9}//焦点Activity为BActivity mFocusedStack=ActivityStack{8238d9 stackId=1, 1 tasks} mLastFocusedStack=ActivityStack{8238d9 stackId=1, 1 tasks} mSleepTimeout=false mCurTaskIdForUser={0=9} mUserStackInFront={} mActivityContainers={0=ActivtyContainer{0}A, 1=ActivtyContainer{1}A} mLockTaskModeState=NONE mLockTaskPackages (userId:packages)= 0:[] mLockTaskModeTasks[] 

1.3 按Home按键退出应用,然后重新打开应用

我们先不调试,我们想想想此时场景下会怎么处理呢!此时的Task相关的调度逻辑如下,此时会先找到已有的相关task,并显示栈顶的Activity,任务调度执行如下所示:

//[ActivityStarter.java] /*
	这里的sourceRecord是指发起调用者 
	r是指本次的将要启动的Activity
	startFlags取值为0
	doResume的值为true
	inTask为发起方指定的任务栈,此时为null
	这里主要确定目标Activity的launchMode Task栈等,即Task的创建和管理
	*/ 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); /*****************************************************************************/ //这里小伙们就不要关注排版问题了,主要是为了演示整个流程,各位就将就一下 private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask, boolean doResume, int startFlags, ActivityRecord sourceRecord, IVoiceInteractionSession 
            							voiceSession, IVoiceInteractor voiceInteractor) { //此处分支会走,将启动过程中的涉及的相关变量进行初始化 reset(); ... mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP;//此时为false mLaunchSingleInstance = r.launchMode == LAUNCH_SINGLE_INSTANCE;//此时为false mLaunchSingleTask = r.launchMode == LAUNCH_SINGLE_TASK;//此时为false //这里重点关注一下,此时mIntent.getFlags携带的flags为0,此处很关键 mLaunchFlags = adjustLaunchFlagsToDocumentMode(r,mLaunchSingleInstance, mLaunchSingleTask, mIntent.getFlags()); ... mDoResume = doResume; ... mInTask = inTask; ... mStartFlags = startFlags;//此时的mStartFlags为0 } /*****************************************************************************/ //根据发起端,计算目标Activity的launchMode模式 computeLaunchingTaskFlags(); /*****************************************************************************/ //这里小伙们就不要关注排版问题了,主要是为了演示整个流程,各位就将就一下 //根据发起者launchMode情况,决定目标Activity的Flags情况 private void computeLaunchingTaskFlags() { //此时场景下的mSourceRecord不为null,mInTask为null,所以不会进入该分支 if (mSourceRecord == null && mInTask != null && mInTask.stack != null) { ... }else {//会进入此分支 mInTask = null; //不满足条件,不会进入该分支 if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null && mSourceRecord.isFreeform()) { mAddingToTask = true; } } } //此时的mInTask为null,会进入此分支,但是该分支下面的三个小分支都不会进入 if (mInTask == null) { if (mSourceRecord == null) {//mSourceRecord不为null,此时的mSourceRecord为Launcher,不会进入此分支 ... } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {//launcher应用activity的启动模式为singleTask ... } else if (mLaunchSingleInstance || mLaunchSingleTask) {//不会进入此分支,此时都为false ... } } /*****************************************************************************/ //确定发起端的ActivityStack情况 computeSourceStack(); /*****************************************************************************/ //这里小伙们就不要关注排版问题了,主要是为了演示整个流程,各位就将就一下  //确定发起端的Stack情况 private void computeSourceStack() { if (mSourceRecord == null) {//mSourceRecord不为null,不会进入此分支 ... } if (!mSourceRecord.finishing) {//此时明显mSourceRecord没有被finish所以会进入此分支 //当调用者Activity不为空,且不处于finishing状态,则其所在栈赋于sourceStack mSourceStack = mSourceRecord.task.stack; return; } ... } /*****************************************************************************/ mIntent.setFlags(mLaunchFlags);//设置目标Activity的launchMode启动模式 // 根据mLaunchFlags来查找是否有可复用的activity /**
	      * 这边主要是判断当前启动的Activity是否存在可以利用的Task
	      * 当启动模式launchMode为singleTask、singleInstance,或者启动时
	      * Flag设置为FLAG_ACTIVITY_NEW_TASK并没设置FLAG_ACTIVITY_MULTIPLE_TASK
	      * 并且当前启动的Activity不是以startActivityForResult启动的,
	      * 满足以上情况才会寻找是否存在有复用的Task。
	      * 匹配规则:
	      * 1、对于启动模式为singleInstance,遍历所有ActivityStack和Task的堆栈中查找
	      *是否存在以当前启动Activity相同的Activity。
	      * 2、其它情况下,遍历所有ActivityStack和Task的堆栈,查找Task中intent变量                                                      * 是否当前启动Activity相匹配,如果不存在,则去匹配task的亲和性(即
	  	   *在AndroidManifest中android:taskAffinity定义的。
	      */ //此时存在复用额Activity,因为我们前面已经创建了目标Activity而且没有被销毁 mReusedActivity = getReusableIntentActivity(); /*****************************************************************************/ //这里小伙们就不要关注排版问题了,主要是为了演示整个流程,各位就将就一下  private ActivityRecord getReusableIntentActivity() { //此时的mLaunchFlags的取值为FLAG_ACTIVITY_RESET_TASK_IF_NEEDED|FLAG_ACTIVITY_NEW_TASK,所以为true boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 && (mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0) || mLaunchSingleInstance || mLaunchSingleTask; //此时mInTask为false,mStartActivity.resultTo为null putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null; ActivityRecord intentActivity = null; if (mOptions != null && mOptions.getLaunchTaskId() != -1) { ... } else if (putIntoExistingTask) { if (mLaunchSingleInstance) {//不会进入此分支 ... } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {//不会进入此分支 ... } else {//会走入此分支,在ASS中查找是否存在启动目标Activity,很明显此时存在,所以intentActivity得到的值为Activity B intentActivity = mSupervisor.findTaskLocked(mStartActivity); } } return intentActivity; } /*****************************************************************************/ //如果找到了可重用的activity,需要清理掉原来的信息,并把当前启动的activity的信息拷贝进去 //做清理和拷贝工作,此时会进入此分支 if (mReusedActivity != null) { ... //设置当前启动Activity的Task为复用的Task,进入此分支 if (mStartActivity.task == null) { mStartActivity.task = mReusedActivity.task; } /*
			 *这边处理启动时设置FLAG_ACTIVITY_CLEAR_TOP时,要清除复用Task中存在与当前启动
			 *Activity相同的Activity之上的Activity
			 *举个例子:比如复用Task1中存在有Activity A,B,C,D,此时正在启动的Activity B,那么C**和D也要finish,另外此时如果B *为标准启动模式,并且没有设置FLAG_ACTIVITY_SINGLE_TOP,那么B也会finish。具体的读者可以跟进
			 *mReusedActivity.task.performClearTaskForReuseLocked看下。
			 */ if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 || mLaunchSingleInstance || mLaunchSingleTask) {//不会进入此分支 ... } // 计算哪个task和activity要移至前台,必要时会进行task的清理工作 mReusedActivity = setTargetStackAndMoveToFrontIfNeeded(mReusedActivity); /*****************************************************************************/ //这里小伙们就不要关注排版问题了,主要是为了演示整个流程,各位就将就一下  private ActivityRecord setTargetStackAndMoveToFrontIfNeeded(ActivityRecord intentActivity) { mTargetStack = intentActivity.task.stack; mTargetStack.mLastPausedActivity = null; final ActivityStack focusStack = mSupervisor.getFocusedStack(); ActivityRecord curTop = (focusStack == null) ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);//获取当前前台ActivityStack栈顶的ActivityRecord  //判断顶部的栈是否符合要求(即判断现在栈顶的栈是否为能够复用的activityrecord所在的栈) if (curTop != null && (curTop.task != intentActivity.task || curTop.task != focusStack.topTask()) && !mAvoidMoveToFront) { //增加一个标记,标识这个task是从任务栈的后面移动上来的 mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); //这里的mSourceRecord表示的是发起端,此处是判断合法性 if (mSourceRecord == null || (mSourceStack.topActivity() != null && mSourceStack.topActivity().task == mSourceRecord.task)) {//次场景会进入此分支 if (mLaunchTaskBehind && mSourceRecord != null) {//此时的mLaunchTaskBehind为null不进入此分支 ... } mMovedOtherTask = true; final boolean willClearTask = (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);//此时willClearTask 的取值为false if (!willClearTask) {//不需要清空,那么就需要将复用的task移至栈顶 //根据规则获取当前要启动activity所属的ActivityStack栈 final ActivityStack launchStack = getLaunchStack( mStartActivity, mLaunchFlags, mStartActivity.task, mOptions); //当要启动的栈与目标一致或者要启动的栈为空。这是我们一般的标准流程。会调用moveTaskToFrontLocked方法,将当前栈移动到与用户交互的栈顶 //此时会进入该分支 if (launchStack == null || launchStack == mTargetStack) { mTargetStack.moveTaskToFrontLocked( intentActivity.task, mNoAnimation, mOptions, mStartActivity.appTimeTracker, "bringingFoundTaskToFront"); mMovedToFront = true; } else if (launchStack.mStackId == DOCKED_STACK_ID || launchStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {//不会进入该分支 ... } mOptions = null; } updateTaskReturnToType(intentActivity.task, mLaunchFlags, focusStack); } } if (!mMovedToFront && mDoResume) {//不进入此分支 ... } mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.task, INVALID_STACK_ID, mTargetStack.mStackId); //此时Luancher启动的时候携带了该值,所以会走入此分支 if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) { return mTargetStack.resetTaskIfNeededLocked(intentActivity, mStartActivity); } ... } /*****************************************************************************/ setTaskFromIntentActivity(mReusedActivity); if (!mAddingToTask && mReuseTask == null) {//进入此分支 resumeTargetStackIfNeeded(); Log.e(ACTIVITY_TAG, "mReusedActivity != null START_TASK_TO_FRONT"); return START_TASK_TO_FRONT; } } } 

至此从按Home按键退出应用,然后重新打开应用的Task以及Stack的调度就完成了,我们对其小结一下,其主要流程可以精简为如下几个步骤:

  • 调用AS.setInitialState方法,对启动目标Activity时Task任务栈和ActivityStack栈将要涉及的变量进行初始化设置
  • 根据发起端Activity的情况,调用computeLaunchingTaskFlags计算目标Activity的launchMode模式
  • 调用computeSourceStack确定发起端Stack的情况
  • 经过上述的一系列处理以后,已经确定目标Activity相关的LauncherMode,flag等启动模式了,接下来调用getReusableIntentActivity确定是否有可复用的Activity,在此场景下存在复用的Activity
  • 进入存在复用Activity的分支,调用setTargetStackAndMoveToFrontIfNeeded处理复用的Activity和Task以及Stack
  • 接着直接调用resumeTargetStackIfNeeded恢复复用Task任务栈

经过如上的步骤以后,我们的目标Activity所属的Task和Stack就已经安排妥当了,我们此时可以通过命令查看相对应的Task任务栈和Stack栈了。

#adb shell dumpsys activity activities
ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities) Display #0 (activities from top to bottom): Stack #1: mFullscreen=true mBounds=null
    Task id #9 mFullscreen=true mBounds=null
    mMinWidth=-1 mMinHeight=-1 mLastNonFullscreenBounds=null * TaskRecord{d361623 #9 A=com.example.test U=0 StackId=1 sz=2} userId=0 effectiveUid=u0a48 mCallingUid=u0a23 mUserSetupComplete=true mCallingPackage=com.android.launcher3
      affinity=com.example.test
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.example.test/.MainActivity} realActivity=com.example.test/.MainActivity
      autoRemoveRecents=false isPersistable=true numFullscreen=2 taskType=0 mTaskToReturnTo=1 rootWasReset=true mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
      Activities=[ActivityRecord{dd0bb9a u0 com.example.test/.AMainActivity t9}, ActivityRecord{59a829e u0 com.example.test/.BActivity t9}] askedCompatMode=false inRecents=true isAvailable=true lastThumbnail=android.graphics.Bitmap@d88386f lastThumbnailFile=/data/system_ce/0/recent_images/9_task_thumbnail.png
      stackId=1 hasBeenVisible=true mResizeMode=RESIZE_MODE_FORCE_RESIZEABLE isResizeable=true firstActiveTime=1602505471813 lastActiveTime=1602505471813 (inactive for 4s) * Hist #1: ActivityRecord{59a829e u0 com.example.test/.BActivity t9} packageName=com.example.test processName=com.example.test
          launchedFromUid=10048 launchedFromPackage=com.example.test userId=0 app=ProcessRecord{cd84720 15340:com.example.test/u0a48} Intent { cmp=com.example.test/.BActivity bnds=[184,356][360,544] } frontOfTask=false task=TaskRecord{d361623 #9 A=com.example.test U=0 StackId=1 sz=2} taskAffinity=com.example.test
          realActivity=com.example.test/.BActivity
          baseDir=/data/app/com.example.test-1/base.apk
          dataDir=/data/user/0/com.example.test
          stateNotNeeded=false componentSpecified=true mActivityType=0 compat={320dpi} labelRes=0x7f050000 icon=0x7f020000 theme=0x7f060001 config={1.0 ?mcc?mnc [zh_CN] ldltr sw360dp w360dp h568dp 320dpi nrml port finger -keyb/v/h -nav/h s.5} taskConfigOverride={1.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?} taskDescription: iconFilename=null label="null" color=ffe6e6e6
          launchFailed=false launchCount=0 lastLaunchTime=-12s222ms
          haveState=false icicle=null
          state=RESUMED stopped=false delayedResume=false finishing=false keysPaused=false inHistory=true visible=true sleeping=false idle=true mStartingWindowState=STARTING_WINDOW_NOT_SHOWN
          fullscreen=true noDisplay=false immersive=false launchMode=0 frozenBeforeDestroy=false forceNewConfig=false mActivityType=APPLICATION_ACTIVITY_TYPE
          waitingVisible=false nowVisible=true lastVisibleTime=-3s634ms
          resizeMode=RESIZE_MODE_FORCE_RESIZEABLE * Hist #0: ActivityRecord{dd0bb9a u0 com.example.test/.MainActivity t9} packageName=com.example.test processName=com.example.test
          launchedFromUid=10023 launchedFromPackage=com.android.launcher3 userId=0 app=ProcessRecord{cd84720 15340:com.example.test/u0a48} Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.example.test/.MainActivity bnds=[184,356][360,544] } frontOfTask=true task=TaskRecord{d361623 #9 A=com.example.test U=0 StackId=1 sz=2} taskAffinity=com.example.test
          realActivity=com.example.test/.MainActivity
          baseDir=/data/app/com.example.test-1/base.apk
          dataDir=/data/user/0/com.example.test
          stateNotNeeded=false componentSpecified=true mActivityType=0 compat={320dpi} labelRes=0x7f050000 icon=0x7f020000 theme=0x7f060001 config={1.0 ?mcc?mnc [zh_CN] ldltr sw360dp w360dp h568dp 320dpi nrml port finger -keyb/v/h -nav/h s.5} taskConfigOverride={1.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?} taskDescription: iconFilename=null label="null" color=ffe6e6e6
          launchFailed=false launchCount=0 lastLaunchTime=-18s855ms
          haveState=true icicle=Bundle[mParcelledData.dataSize=1132] state=STOPPED stopped=true delayedResume=false finishing=false keysPaused=false inHistory=true visible=false sleeping=false idle=true mStartingWindowState=STARTING_WINDOW_SHOWN
          fullscreen=true noDisplay=false immersive=false launchMode=0 frozenBeforeDestroy=false forceNewConfig=false mActivityType=APPLICATION_ACTIVITY_TYPE
          waitingVisible=false nowVisible=false lastVisibleTime=-18s367ms
          resizeMode=RESIZE_MODE_FORCE_RESIZEABLE

    Running activities (most recent first): TaskRecord{d361623 #9 A=com.example.test U=0 StackId=1 sz=2} Run #1: ActivityRecord{59a829e u0 com.example.test/.BActivity t9} Run #0: ActivityRecord{dd0bb9a u0 com.example.test/.MainActivity t9} mResumedActivity: ActivityRecord{59a829e u0 com.example.test/.BActivity t9} Stack #0: mFullscreen=true mBounds=null
    Task id #8 mFullscreen=true mBounds=null
    mMinWidth=-1 mMinHeight=-1 mLastNonFullscreenBounds=null * TaskRecord{3a6c4af #8 A=com.android.launcher3 U=0 StackId=0 sz=1} userId=0 effectiveUid=u0a23 mCallingUid=1000 mUserSetupComplete=true mCallingPackage=android
      affinity=com.android.launcher3
      intent={act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.launcher3/.Launcher} realActivity=com.android.launcher3/.Launcher
      autoRemoveRecents=false isPersistable=true numFullscreen=1 taskType=1 mTaskToReturnTo=0 rootWasReset=false mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
      Activities=[ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8}] askedCompatMode=false inRecents=true isAvailable=true lastThumbnail=null lastThumbnailFile=/data/system_ce/0/recent_images/8_task_thumbnail.png
      stackId=0 hasBeenVisible=true mResizeMode=RESIZE_MODE_FORCE_RESIZEABLE isResizeable=false firstActiveTime=1602505471784 lastActiveTime=1602505471784 (inactive for 4s) * Hist #0: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8} packageName=com.android.launcher3 processName=com.android.launcher3
          launchedFromUid=0 launchedFromPackage=null userId=0 app=ProcessRecord{c4940bc 14811:com.android.launcher3/u0a23} Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.launcher3/.Launcher } frontOfTask=true task=TaskRecord{3a6c4af #8 A=com.android.launcher3 U=0 StackId=0 sz=1} taskAffinity=com.android.launcher3
          realActivity=com.android.launcher3/.Launcher
          baseDir=/system/app/PaxLauncher3/PaxLauncher3.apk
          dataDir=/data/user/0/com.android.launcher3
          stateNotNeeded=true componentSpecified=false mActivityType=1 compat={320dpi} labelRes=0x7f0a0001 icon=0x7f030001 theme=0x7f0d0002 config={1.0 ?mcc?mnc [zh_CN] ldltr sw360dp w360dp h568dp 320dpi nrml port finger -keyb/v/h -nav/h s.5} taskConfigOverride={1.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?} taskDescription: iconFilename=null label="null" color=ff222222
          launchFailed=false launchCount=0 lastLaunchTime=-1m38s596ms
          haveState=true icicle=Bundle[mParcelledData.dataSize=3788] state=STOPPED stopped=true delayedResume=false finishing=false keysPaused=false inHistory=true visible=false sleeping=false idle=true mStartingWindowState=STARTING_WINDOW_NOT_SHOWN
          fullscreen=true noDisplay=false immersive=false launchMode=2 frozenBeforeDestroy=false forceNewConfig=false mActivityType=HOME_ACTIVITY_TYPE
          waitingVisible=false nowVisible=false lastVisibleTime=-5s891ms
          resizeMode=RESIZE_MODE_FORCE_RESIZEABLE

    Running activities (most recent first): TaskRecord{3a6c4af #8 A=com.android.launcher3 U=0 StackId=0 sz=1} Run #0: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8} mLastPausedActivity: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8} mFocusedActivity: ActivityRecord{59a829e u0 com.example.test/.BActivity t9} mFocusedStack=ActivityStack{8238d9 stackId=1, 1 tasks} mLastFocusedStack=ActivityStack{8238d9 stackId=1, 1 tasks} mSleepTimeout=false mCurTaskIdForUser={0=9} mUserStackInFront={} mActivityContainers={0=ActivtyContainer{0}A, 1=ActivtyContainer{1}A} mLockTaskModeState=NONE mLockTaskPackages (userId:packages)= 0:[] mLockTaskModeTasks[] 


总结

Activity启动流程(三)- Activity Task调度算法复盘分析到这里就要告一段落了,从前面的分析可以看出来,Activity和Task的调度算法非常复杂,最好需结合实际场景才好分析,只有这样才知道是否需要新建Task,还是将新的ActivityRecord加入到已有的Task里。当前这一切的前提条件是我们能理解启动模式的一些特点,这样才能对理解调度算法有一个基础,如果一上来就是懵懵懂懂的乱干那就完蛋了,因为你会被这源码绕的晕头转向的搞不清方向了。好了今天就到这里了,希望小伙们能点赞和关注,谢谢!

本文地址:https://blog.csdn.net/tkwxty/article/details/108995543