Android四大组件之Activity启动流程源码实现详解
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
在正式开始今天博客相关源码分析前,还是先奉上调用的时序图以便小伙们先从整体上有个清晰的概括,然后再从细节开撸!
一.实际场景一复盘分析
假设我们存在如下的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