Android四大元件原始碼實現詳解系列部落格目錄:
Android應用程序建立流程大揭祕
Android四大元件之bindService原始碼實現詳解
Android四大元件之Activity啟動流程原始碼實現詳解概要
Android四大元件之Activity啟動流程原始碼實現詳解(一)
Android四大元件之Activity啟動流程原始碼實現詳解(二)
Activity啟動流程(三)- Activity Task排程演演算法覆盤分析
還記得我們在前面部落格Android四大元件之Activity啟動流程原始碼實現詳解(二)中做的艱苦卓越的鬥爭嗎!這場戰役之慘烈,戰況之持久前所未有!雖然過程是疼苦的,但是戰果也是顯赫和令人滿意的,通過上述戰役我們取得了如下的階段性成果:
總而言之經過上述一頓猛虎般的操作,此時要啟動的目標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
並且在後續的原始碼分析過程中為了簡述方便,會將做如下簡述:
在正式開始今天部落格相關原始碼分析前,還是先奉上呼叫的時序圖以便小夥們先從整體上有個清晰的概括,然後再從細節開擼!
假設我們存在如下的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的排程!
在正式啟動前,我們先來看看此時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排程就完成了,我們對其小結一下,其主要流程可以精簡為如下幾個步驟:
經過如上的步驟以後,我們的目標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[]
通過這種方式啟動目標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的排程就完成了,我們對其小結一下,其主要流程可以精簡為如下幾個步驟:
經過如上的步驟以後,我們的目標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[]
我們先不偵錯,我們想想想此時場景下會怎麼處理呢!此時的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的排程就完成了,我們對其小結一下,其主要流程可以精簡為如下幾個步驟:
經過如上的步驟以後,我們的目標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裡。當前這一切的前提條件是我們能理解啟動模式的一些特點,這樣才能對理解排程演演算法有一個基礎,如果一上來就是懵懵懂懂的亂幹那就完蛋了,因為你會被這原始碼繞的暈頭轉向的搞不清方向了。好了今天就到這裡了,希望小夥們能點贊和關注,謝謝!