Android四大元件之Activity啟動流程原始碼實現詳解(三)

2020-10-13 13:00:08

  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裡。當前這一切的前提條件是我們能理解啟動模式的一些特點,這樣才能對理解排程演演算法有一個基礎,如果一上來就是懵懵懂懂的亂幹那就完蛋了,因為你會被這原始碼繞的暈頭轉向的搞不清方向了。好了今天就到這裡了,希望小夥們能點贊和關注,謝謝!