主頁 > 前端設計 > Android四大組件之Activity啟動流程原始碼實作詳解(三)

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

2020-10-13 11:03:12 前端設計

??Activity啟動流程(三)- Activity Task調度演算法復盤分析

Android四大組件原始碼實作詳解系列博客目錄:

Android應用行程創建流程大揭秘
Android四大組件之bindService原始碼實作詳解
Android四大組件之Activity啟動流程原始碼實作詳解概要
Android四大組件之Activity啟動流程原始碼實作詳解(一)
Android四大組件之Activity啟動流程原始碼實作詳解(二)
Activity啟動流程(三)- Activity Task調度演算法復盤分析


前言

??還記得我們在前面博客Android四大組件之Activity啟動流程原始碼實作詳解(二)中做的艱苦卓越的斗爭嗎!這場戰役之慘烈,戰況之持久前所未有!雖然程序是疼苦的,但是戰果也是顯赫和令人滿意的,通過上述戰役我們取得了如下的階段性成果:

  • 初始化了Activity啟動狀態
  • 計算了啟動launchFlag
  • 計算了呼叫者的ActivityStack
  • 檢查了是否存在復用的TaskRecord
  • 對于存在復用的TaskRecord則進行相應的ActivityStack、TaskRecord的移動(說實話,我也沒有真的搞懂,希望這塊比較有經驗的小伙們能和我一起學習)
  • 計算了當前啟動Activity所屬的TaskRecord
  • 把當前啟動的Activity放到所屬TaskRecord的堆疊頂
  • 并且前面的TaskRecord放到了ActivityStack的堆疊頂

總而言之經過上述一頓猛虎般的操作,此時要啟動的目標Actvity及其對應的task位置以及ActivityStack已經安排妥當,現在可以準備接下來的相關作業了,本來我也是準備這么干的,可是總感覺分析少了點什么!通過前面的分析我們知道了ActivityStack類的startActivityUncheckedLocked方法負責調度ActivityRecord以及TaskRecord,并且通過前面的分析我們也可以知道調度演算法非常復雜,最好需結合實際場景分析調度演算法,但是前面我們只是對startActivityUncheckedLocked整個原始碼進行了流水式的分析,而沒有結合實際,所以本篇博客將結合實際場景分析調度演算法,將Activity啟動程序中涉及的TaskRecord和ActivityStack的調度再來復盤一下!

注意:本篇的介紹是基于Android 7.xx平臺為基礎的,其中涉及的代碼路徑如下:

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

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

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

并且在后續的原始碼分析程序中為了簡述方便,會將做如下簡述:

  • ApplicationThreadProxy簡稱為ATP
  • ActivityManagerProxy簡稱為AMP
  • ActivityManagerService簡稱為AMS
  • ActivityManagerNative簡稱AMN
  • ApplicationThreadNative簡稱ATN
  • PackageManagerService簡稱為PKMS
  • ApplicationThread簡稱為AT
  • ActivityStarter簡稱為AS,這里不要和ActivityServices搞混淆了
  • ActivityStackSupervisor簡稱為ASS

在正式開始今天博客相關原始碼分析前,還是先奉上呼叫的時序圖以便小伙們先從整體上有個清晰的概括,然后再從細節開擼!

在這里插入圖片描述



一.實際場景一復盤分析

??假設我們存在如下的Activity啟動場景某應用內有兩個Activity,A和B,A為該應用入口Activity,從A可跳轉至B,A和B的啟動模式都為standard,我們以該場景為切入口來分析Activity啟動程序中Task任務堆疊和Activity堆疊的調度,本章接將會重點圍繞如下三種Activity啟動情況分析:

  • 從Launcher桌面第一次啟動應用時的任務調度情況:

  • 任務調度時會創建新task,并將新的ActivityRecord加入這個新的task,然后將task放入合適的Stack的堆疊頂

  • 應用內Activity跳轉時的任務調度情況:

  • 任務調度時會將新的ActivityRecord加入已有的task,然后將該ActivityRecord移動到Task頂端,然后將task放入合適的Stack的堆疊頂

  • 然后按Home鍵,再打開應用程式時的調度情況:

  • 任務調度時會先找到已有的相關task,并顯示堆疊頂的Activity

通過前面的博客Android四大組件之Activity啟動流程原始碼實作詳解(二)我們知道Activity啟動程序中Task和ActivityStack的調度涉及的代碼眾多,所以在復盤的程序中我們只抽取重點,并將AS.startActivityUnchecked中的涉及的方法,我們將采用偽代碼的方式進行分析,只為了小伙們們能更好的了解Task和ActivityStack的調度!


1.1 從Launcher桌面第一次啟動應用時Activity的Task以及Stack調度情況

??在正式啟動前,我們先來看看此時Android終端中的各種Task和ActivityStack的情況,我們可以通過Android內置的命令dumpsys activity activities來進行查看,此時的Activity的堆疊以及Task情況如下:

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

    Running activities (most recent first):
      TaskRecord{3a6c4af #8 A=com.android.launcher3 U=0 StackId=0 sz=1}
        Run #0: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8}

    mResumedActivity: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8}
    mLastPausedActivity: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8}

  mFocusedActivity: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8}
  mFocusedStack=ActivityStack{a520845 stackId=0, 1 tasks} mLastFocusedStack=ActivityStack{a520845 stackId=0, 1 tasks}
  mSleepTimeout=false
  mCurTaskIdForUser={0=8}
  mUserStackInFront={}
  mActivityContainers={0=ActivtyContainer{0}A}
  mLockTaskModeState=NONE mLockTaskPackages (userId:packages)=
    0:[]
 mLockTaskModeTasks[]

通過前面的dump,此時我們可以知道了ASS中的mHomeStack已經創建了,并且Launcher桌面Activity對應的Task和ActivityRecord都已經OK了!但是由于Launcher桌面Activity的啟動不在本篇的分析中,它對應的Activity的Task以及相關的調度就不予分析了!

從Launcher桌面第一次啟動應用時Activity的Task以及Stack調度執行如下所示:

//[ActivityStarter.java]
	/*
	這里的sourceRecord是指發起呼叫者 
	r是指本次的將要啟動的Activity
	startFlags取值為0
	doResume的值為true
	inTask為發起方指定的任務堆疊,此時為null
	這里主要確定目標Activity的launchMode Task堆疊等,即Task的創建和管理
	*/
    private int startActivityUnchecked(final ActivityRecord r, 
    									ActivityRecord sourceRecord,
            							IVoiceInteractionSession voiceSession, 
            							IVoiceInteractor voiceInteractor,
            							int startFlags, 
            							boolean doResume, 
            							ActivityOptions options, 
            							TaskRecord inTask) {

		//設定初始化狀態,此時需要重點關注該方法中的如下幾個目標值的獲取,為了分析的方便,我會將該方法展開
		setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
		              voiceInteractor);
        /*****************************************************************************/
        	//這里小伙們就不要關注排版問題了,主要是為了演示整個流程,各位就將就一下
			private void setInitialState(ActivityRecord r, 
										ActivityOptions options, 
										TaskRecord inTask,
            							boolean doResume, 
            							int startFlags, 
            							ActivityRecord sourceRecord,
            							IVoiceInteractionSession 
            							voiceSession, 
            							IVoiceInteractor voiceInteractor) {
						//此處分支會走,將啟動程序中的涉及的相關變數進行初始化
						reset();     
						...   
						mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP;//此時為false
						mLaunchSingleInstance = r.launchMode == LAUNCH_SINGLE_INSTANCE;//此時為false
						mLaunchSingleTask = r.launchMode == LAUNCH_SINGLE_TASK;//此時為false
						//這里重點關注一下,此時mIntent.getFlags攜帶的為FLAG_ACTIVITY_RESET_TASK_IF_NEEDED|FLAG_ACTIVITY_NEW_TASK,這個地方是關鍵因為后面會根據這些值進行Task和Stack的調度
						mLaunchFlags = adjustLaunchFlagsToDocumentMode(r,mLaunchSingleInstance, mLaunchSingleTask, mIntent.getFlags());
						...
						mDoResume = doResume;
						...
						mInTask = inTask;
						...
						mStartFlags = startFlags;//此時的mStartFlags為0
      		}
        /*****************************************************************************/

		//根據發起端,計算目標Activity的launchMode模式
		computeLaunchingTaskFlags();
		/*****************************************************************************/
			//這里小伙們就不要關注排版問題了,主要是為了演示整個流程,各位就將就一下
			//根據發起者launchMode情況,決定目標Activity的Flags情況
    		private void computeLaunchingTaskFlags() {
    			//此時場景下的mSourceRecord不為null,mInTask為null,所以不會進入該分支
    			if (mSourceRecord == null && mInTask != null && mInTask.stack != null) {
    				...
    			}else {//會進入此分支
		            mInTask = null;
		            //不滿足條件,不會進入該分支
		            if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null
		                    && mSourceRecord.isFreeform())  {
		                	...
		            }
    			}
        	}
        	//此時的mInTask為null,會進入此分支,但是該分支下面的三個小分支都不會進入
        	if (mInTask == null) {
	            if (mSourceRecord == null) {//mSourceRecord不為null,此時的mSourceRecord為Launcher,不會進入此分支
					...
	            } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {//launcher應用activity的啟動模式為singleTask
					...
	            } else if (mLaunchSingleInstance || mLaunchSingleTask) {//不會進入此分支,此時都為false
					...
	            }
        	}
		/*****************************************************************************/

		//確定發起端的ActivityStack情況
		computeSourceStack();
		/*****************************************************************************/
			//這里小伙們就不要關注排版問題了,主要是為了演示整個流程,各位就將就一下     
			//確定發起端的Stack情況
	    	private void computeSourceStack() {
		        if (mSourceRecord == null) {//mSourceRecord不為null,不會進入此分支
					...
		        }
		        if (!mSourceRecord.finishing) {//此時明顯mSourceRecord沒有被finish所以會進入此分支
					//當呼叫者Activity不為空,且不處于finishing狀態,則其所在堆疊賦于sourceStack
		            mSourceStack = mSourceRecord.task.stack;
		            return;
		        }
				...
	    	}   
   	  /*****************************************************************************/	

		mIntent.setFlags(mLaunchFlags);//設定目標Activity的launchMode啟動模式

		 // 根據mLaunchFlags來查找是否有可復用的activity
		 /**
	      * 這邊主要是判斷當前啟動的Activity是否存在可以利用的Task
	      * 當啟動模式launchMode為singleTask、singleInstance,或者啟動時
	      * Flag設定為FLAG_ACTIVITY_NEW_TASK并沒設定FLAG_ACTIVITY_MULTIPLE_TASK
	      * 并且當前啟動的Activity不是以startActivityForResult啟動的,
	      * 滿足以上情況才會尋找是否存在有復用的Task,
	      * 匹配規則:
	      * 1、對于啟動模式為singleInstance,遍歷所有ActivityStack和Task的堆疊中查找
	      *是否存在以當前啟動Activity相同的Activity,
	      * 2、其它情況下,遍歷所有ActivityStack和Task的堆疊,查找Task中intent變數                                                      * 是否當前啟動Activity相匹配,如果不存在,則去匹配task的親和性(即
	  	   *在AndroidManifest中android:taskAffinity定義的,
	      */
		 //此時肯定不存在復用的Activity,因為除開啟動了Launcher對應的Activity,啥都還沒有啟動呢
		 mReusedActivity = getReusableIntentActivity();
		/*****************************************************************************/	
			//這里小伙們就不要關注排版問題了,主要是為了演示整個流程,各位就將就一下 	 
			 private ActivityRecord getReusableIntentActivity() {
			 	  //此時的mLaunchFlags的取值為FLAG_ACTIVITY_RESET_TASK_IF_NEEDED|FLAG_ACTIVITY_NEW_TASK,所以為true
			      boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
			              (mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
			              || mLaunchSingleInstance || mLaunchSingleTask;
			
				  //此時mInTask為false,mStartActivity.resultTo為null
			      putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
			      ActivityRecord intentActivity = null;
			      if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
			      	...
			      } else if (putIntoExistingTask) {
			          if (mLaunchSingleInstance) {//不會進入此分支
					...
			          } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {//不會進入此分支
					...
			          } else {//會走入此分支,在ASS中查找是否存在啟動目標Activity,很明顯此時不存在,所以intentActivity得到的值為null
			              intentActivity = mSupervisor.findTaskLocked(mStartActivity);
			          }
			      }
			      return intentActivity;
			  }
	    /*****************************************************************************/		

		...
		//如果找到了可重用的activity,則進行下一步相關操作,在此場景下很明顯沒有找到可復用的Activity
		if (mReusedActivity != null) {//不會進入此分支,忽略
			...
		}  

        if (mStartActivity.packageName == null) {//例外處理,正常啟動不會進入此分支忽略
			...
        }


		//是否需要啟動新的Activity標記,此場景下dontStart為false
        final boolean dontStart = top != null && mStartActivity.resultTo == null
                && top.realActivity.equals(mStartActivity.realActivity)
                && top.userId == mStartActivity.userId
                && top.app != null && top.app.thread != null
                && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                || mLaunchSingleTop || mLaunchSingleTask);
		
        if (dontStart) {//不會走入此分支,忽略
			...
        }

		//表示是否需要創建新的任務堆疊
        boolean newTask = false;
		...

      
        /*
        	如果要啟動的目標Activity沒有對應的resultTo,很明顯由于mLaunchFlags攜帶FLAG_ACTIVITY_NEW_TASK所以result會被置為null
        	并且也沒有添加到對應堆疊中,mAddingToTask為false
        	而且設定了FLAG_ACTIVITY_NEW_TASK,
        	說明沒有找到對應的堆疊來啟動我們的Activity,
            所以會通過創建或者復用一個堆疊來存放Activity
            此場景下會進入該分支
        */
        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
                && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {//此場景會進入此分支
            newTask = true;
			// 重用啟動端Activity所屬Task或者新建task
            setTaskFromReuseOrCreateNewTask(taskToAffiliate);
			/*****************************************************************************/	
			//這里小伙們就不要關注排版問題了,主要是為了演示整個流程,各位就將就一下 	 
				//從發起端獲取Task任務堆疊或者新建一個任務堆疊
				//此處的入參taskToAffiliate為null
			    private void setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate) {
			    	//獲取目標ActivityStack堆疊,即目標Activity所屬的Stack堆疊
			        mTargetStack = computeStackFocus(mStartActivity, true, mLaunchBounds, mLaunchFlags,
			                mOptions);
			
			        if (mReuseTask == null) {//此時mReuseTask為null,會進入該分支
						//創建新的Task
			            final TaskRecord task = mTargetStack.createTaskRecord(
			                    mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
			                    mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
			                    mNewTaskIntent != null ? mNewTaskIntent : mIntent,
			                    mVoiceSession, mVoiceInteractor, !mLaunchTaskBehind /* toTop */);
			            //將創建的Task設定為目標Activity的Task
			            mStartActivity.setTask(task, taskToAffiliate);
			            if (mLaunchBounds != null) {//此時的mLaunchBounds為null,不會進入此分支
							...
			            }
			        } else {//不會進入此分支,因為此時的mReuseTask為null,在setInitialState被設定的,后續沒有被修改過
			            mStartActivity.setTask(mReuseTask, taskToAffiliate);
			        }
			    }
			/*****************************************************************************/
			...
            if (!mMovedOtherTask) {//會走入此分支
                updateTaskReturnToType(mStartActivity.task, mLaunchFlags, topStack);
            }
        }
		/*
			當mSourceRecord不為空,把新的ActivityRecord系結到啟動者的TaskRecord上,
         	一般情況下,mSourceRecord就是呼叫者,如本例中的Launcher;
        	但也有特殊情況,舉個例子,如果啟動模式為singleTask,堆疊中又不存在相同的Activity時,
        	mSourceRecord就是堆疊頂的Activity
		*/
		else if (mSourceRecord != null) {//不會進入此分支
			...
        } else if (mInTask != null) {//啟動時指定了目標堆疊(mInTask),ActivityRecord系結到mInTask,不會進入此分支
			...
        } else {//不會進入此分支
			...
        }	

		//權限檢測
        mService.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName,
                mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);

        if (mSourceRecord != null && mSourceRecord.isRecentsActivity()) {//不會走入此分支,因為我們的發起端是Luancher不屬于RecentsActivity
			...
        }

		...
		/*把當前啟動的Activity加入TaskRecord以及系結WindowManagerService*/
        mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);	
		/*****************************************************************************/	
			//這里小伙們就不要關注排版問題了,主要是為了演示整個流程,各位就將就一下  
		    final void startActivityLocked(	ActivityRecord r, //此時的r為目標Activity
		    								boolean newTask, //newTask表示是否要創建Task,為true
		    								boolean keepCurTransition,
		            						ActivityOptions options) 
		   {
		        TaskRecord rTask = r.task;
		        final int taskId = rTask.taskId;
				
		        if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {//會進入此分支
		            //task中的上一個activity已被移除,或者ams重用該task,則將該task移到頂部
		            insertTaskAtTop(rTask, r);//將前面創建的Task放到Stack的頂部
		            mWindowManager.moveTaskToTop(taskId);
		        }
		        TaskRecord task = null;
		        if (!newTask) {//newTask為true不會走此分支
					...
		        }
		
				...
		
		        task = r.task;
		
				//將Activity移動到Stack的頂端
		        task.addActivityToTop(r);
		        task.setFrontOfTask();
		
		        r.putInHistory();
		        if (!isHomeStack() || numActivities() > 0) {//會進入此分支,此時的ActivityStack不是HomeStack
					//這個地方很重要
		            addConfigOverride(r, task);
		        } else {//不會進入此分支
					...
		        }
		        ...
		    }	
		/*****************************************************************************/		
		...	    		      
}

至此從Launcher桌面第一次啟動應用時Activity的Task以及Stack調度就完成了,我們對其小結一下,其主要流程可以精簡為如下幾個步驟:

  • 呼叫AS.setInitialState方法,對啟動目標Activity時Task任務堆疊和ActivityStack堆疊將要涉及的變數進行初始化設定
  • 根據發起端Activity的情況,呼叫computeLaunchingTaskFlags計算目標Activity的launchMode模式
  • 呼叫computeSourceStack確定發起端Stack的情況
  • 經過上述的一系列處理以后,已經確定目標Activity相關的LauncherMode,flag等啟動模式了,接下來呼叫getReusableIntentActivity確定是否有可復用的Activity,在此場景下肯定不會存在可以復用的Activity
  • 接著呼叫方法setTaskFromReuseOrCreateNewTask創建目標Activity對應的Task和找到合適的Stack,并且將創建的Task移動到目標Stack的頂端
  • 接著繼續呼叫ASS.startActivityLocked方法,將目標Activity加入TaskRecord,并且將其放入對應ActivityStack的頂部以及系結WindowManagerService

經過如上的步驟以后,我們的目標Activity所屬的Task和Stack就已經安排妥當了,我們此時可以通過命令查看,可以看到此時的mFocusedActivity和mFocusedStack為目標Activity,

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

    Running activities (most recent first):
      TaskRecord{d361623 #9 A=com.example.test U=0 StackId=1 sz=1}
        Run #0: ActivityRecord{dd0bb9a u0 com.example.test/.AMainActivity t9}

    mResumedActivity: ActivityRecord{dd0bb9a u0 com.example.test/.AMainActivity t9}

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

    Running activities (most recent first):
      TaskRecord{3a6c4af #8 A=com.android.launcher3 U=0 StackId=0 sz=1}
        Run #0: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8}

    mLastPausedActivity: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8}

  mFocusedActivity: ActivityRecord{dd0bb9a u0 com.example.test/.AMainActivity t9}
  mFocusedStack=ActivityStack{8238d9 stackId=1, 1 tasks} mLastFocusedStack=ActivityStack{8238d9 stackId=1, 1 tasks}
  mSleepTimeout=false
  mCurTaskIdForUser={0=9}
  mUserStackInFront={}
  mActivityContainers={0=ActivtyContainer{0}A, 1=ActivtyContainer{1}A}
  mLockTaskModeState=NONE mLockTaskPackages (userId:packages)=
    0:[]
 mLockTaskModeTasks[]


1.2 從已經啟動應用的A Activity跳轉到B Activity

??通過這種方式啟動目標Activity較前面的啟動方式要簡單一些,因為無需創建新的Task了,只需要將目標Activity加入到A所屬的Task即可,注意此時啟動B Activity是標準的啟動方式,此時啟動目標B Activity的Task以及Stack調度執行如下所示:

//[ActivityStarter.java]
	/*
	這里的sourceRecord是指發起呼叫者 
	r是指本次的將要啟動的Activity
	startFlags取值為0
	doResume的值為true
	inTask為發起方指定的任務堆疊,此時為null
	這里主要確定目標Activity的launchMode Task堆疊等,即Task的創建和管理
	*/
    private int startActivityUnchecked(final ActivityRecord r, 
    									ActivityRecord sourceRecord,
            							IVoiceInteractionSession voiceSession, 
            							IVoiceInteractor voiceInteractor,
            							int startFlags, 
            							boolean doResume, 
            							ActivityOptions options, 
            							TaskRecord inTask) {

		//設定初始化狀態,此時需要重點關注該方法中的如下幾個目標值的獲取,為了分析的方便,我會將該方法展開
		setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
		              voiceInteractor);
        /*****************************************************************************/
        	//這里小伙們就不要關注排版問題了,主要是為了演示整個流程,各位就將就一下
			private void setInitialState(ActivityRecord r, 
										ActivityOptions options, 
										TaskRecord inTask,
            							boolean doResume, 
            							int startFlags, 
            							ActivityRecord sourceRecord,
            							IVoiceInteractionSession 
            							voiceSession, 
            							IVoiceInteractor voiceInteractor) {
						//此處分支會走,將啟動程序中的涉及的相關變數進行初始化
						reset();     
						...   
						mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP;//此時為false
						mLaunchSingleInstance = r.launchMode == LAUNCH_SINGLE_INSTANCE;//此時為false
						mLaunchSingleTask = r.launchMode == LAUNCH_SINGLE_TASK;//此時為false
						//這里重點關注一下,此時mIntent.getFlags攜帶的flags為0,此處很關鍵
						mLaunchFlags = adjustLaunchFlagsToDocumentMode(r,mLaunchSingleInstance, mLaunchSingleTask, mIntent.getFlags());
						...
						mDoResume = doResume;
						...
						mInTask = inTask;
						...
						mStartFlags = startFlags;//此時的mStartFlags為0
      		}
        /*****************************************************************************/
		//根據發起端,計算目標Activity的launchMode模式
		computeLaunchingTaskFlags();
		/*****************************************************************************/
			//這里小伙們就不要關注排版問題了,主要是為了演示整個流程,各位就將就一下
			//根據發起者launchMode情況,決定目標Activity的Flags情況
    		private void computeLaunchingTaskFlags() {
    			//此時場景下的mSourceRecord不為null,mInTask為null,所以不會進入該分支
    			if (mSourceRecord == null && mInTask != null && mInTask.stack != null) {
    				...
    			}else {//會進入此分支
		            mInTask = null;
		            //不滿足條件,不會進入該分支
		            if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null
		                    && mSourceRecord.isFreeform())  {
		                	mAddingToTask = true;
		            }
    			}
        	}
        	//此時的mInTask為null,會進入此分支,但是該分支下面的三個小分支都不會進入
        	if (mInTask == null) {
	            if (mSourceRecord == null) {//mSourceRecord不為null,此時的mSourceRecord為Launcher,不會進入此分支
					...
	            } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {//launcher應用activity的啟動模式為singleTask
					...
	            } else if (mLaunchSingleInstance || mLaunchSingleTask) {//不會進入此分支,此時都為false
					...
	            }
        	}
		/*****************************************************************************/    
		//確定發起端的ActivityStack情況
		computeSourceStack();
		/*****************************************************************************/
			//這里小伙們就不要關注排版問題了,主要是為了演示整個流程,各位就將就一下     
			//確定發起端的Stack情況
	    	private void computeSourceStack() {
		        if (mSourceRecord == null) {//mSourceRecord不為null,不會進入此分支
					...
		        }
		        if (!mSourceRecord.finishing) {//此時明顯mSourceRecord沒有被finish所以會進入此分支
					//當呼叫者Activity不為空,且不處于finishing狀態,則其所在堆疊賦于sourceStack
		            mSourceStack = mSourceRecord.task.stack;
		            return;
		        }
				...
	    	}   
   	  /*****************************************************************************/	

		mIntent.setFlags(mLaunchFlags);//設定目標Activity的launchMode啟動模式

		 // 根據mLaunchFlags來查找是否有可復用的activity
		 /**
	      * 這邊主要是判斷當前啟動的Activity是否存在可以利用的Task
	      * 當啟動模式launchMode為singleTask、singleInstance,或者啟動時
	      * Flag設定為FLAG_ACTIVITY_NEW_TASK并沒設定FLAG_ACTIVITY_MULTIPLE_TASK
	      * 并且當前啟動的Activity不是以startActivityForResult啟動的,
	      * 滿足以上情況才會尋找是否存在有復用的Task,
	      * 匹配規則:
	      * 1、對于啟動模式為singleInstance,遍歷所有ActivityStack和Task的堆疊中查找
	      *是否存在以當前啟動Activity相同的Activity,
	      * 2、其它情況下,遍歷所有ActivityStack和Task的堆疊,查找Task中intent變數                                                      * 是否當前啟動Activity相匹配,如果不存在,則去匹配task的親和性(即
	  	   *在AndroidManifest中android:taskAffinity定義的,
	      */
		 //此時肯定不存在復用的Activity,因為除開啟動了A對應的Activity,啥都還沒有啟動呢
		 mReusedActivity = getReusableIntentActivity();
		/*****************************************************************************/	
			//這里小伙們就不要關注排版問題了,主要是為了演示整個流程,各位就將就一下 	
		    private ActivityRecord getReusableIntentActivity() {
		    	//此時的mLaunchFlags為0,而mLaunchSingleInstance為false,mLaunchSingleTask也為false,肯定不會存在可以復用的Activity
		        boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
		                (mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
		                || mLaunchSingleInstance || mLaunchSingleTask;
		        putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
		        ActivityRecord intentActivity = null;
		        if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
					...
		        } else if (putIntoExistingTask) {
					...
		        }
		        return intentActivity;
		    }			 	
		/*****************************************************************************/		

		//如果找到了可重用的activity,則進行下一步相關操作,在此場景下很明顯沒有找到可復用的Activity
		if (mReusedActivity != null) {//不會進入此分支,忽略
			...
		}  

        if (mStartActivity.packageName == null) {//例外處理,正常啟動不會進入此分支忽略
			...
        }


		//是否需要啟動新的Activity標記,此場景下dontStart為false
        final boolean dontStart = top != null && mStartActivity.resultTo == null
                && top.realActivity.equals(mStartActivity.realActivity)
                && top.userId == mStartActivity.userId
                && top.app != null && top.app.thread != null
                && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                || mLaunchSingleTop || mLaunchSingleTask);
		
        if (dontStart) {//不會走入此分支,忽略
			...
        }

		//表示是否需要創建新的任務堆疊
        boolean newTask = false;
		...		

        /*
        	如果要啟動的目標Activity沒有對應的resultTo,
        	并且也沒有添加到對應堆疊中
        	而且設定了FLAG_ACTIVITY_NEW_TASK,
        	說明沒有找到對應的堆疊來啟動我們的Activity,
            所以會通過創建或者復用一個堆疊來存放Activity
        */
        //不會進入此分支
        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
                && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
			...				...
        }
		/*
			當mSourceRecord不為空,把新的ActivityRecord系結到啟動者的TaskRecord上,
			此場景下會走入此分支,從發起端Activity A獲取Task以及它所屬的Stack
		*/	    
		else if (mSourceRecord != null) {

			// 不是新建task的,重用原activity的task
            final int result = setTaskFromSourceRecord();
			/*****************************************************************************/	
				//這里小伙們就不要關注排版問題了,主要是為了演示整個流程,各位就將就一下 
			    private int setTaskFromSourceRecord() {
					//獲取啟動Activity的任務堆疊
			        final TaskRecord sourceTask = mSourceRecord.task;
			        //此時的發起端Actiivty所在的TaskRecord就是處于sourceStack堆疊頂,所以sourceStack.topTask就是要啟動的Activity所在的堆疊
			        //如果目標Activity不允許在螢屏上顯示或者源任務堆疊和目標任務不在同一個堆疊
			        final boolean moveStackAllowed = sourceTask.stack.topTask() != sourceTask;
					//獲取當前要啟動activity所屬的ActivityStack堆疊
			        if (moveStackAllowed) {//不會進入此分支						
						...
			        }
					
					//目標ActivityStack為空
			        if (mTargetStack == null) {
			            mTargetStack = sourceTask.stack;//進入此分支
			        } else if (mTargetStack != sourceTask.stack) {
			        	//把啟動方的任務堆疊系結到目標ActivityStack上
						...
			        }
			        if (mDoResume) {
			            mTargetStack.moveToFront("sourceStackToFront");
			        }
			
					//獲取目標ActivityStack的頂部task
			        final TaskRecord topTask = mTargetStack.topTask();
			        if (topTask != sourceTask && !mAvoidMoveToFront) {//不會走入此分支
						
			        }
			
					//如果目標activity還沒有加入到堆疊中,而且啟動標志設定了CLEAR_TOP,那么我們將Activity添加到已經存在的任務堆疊中,并呼叫clear方法清空對應的activity
			        if (!mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0) {//很明顯不會進入此分支
						...
			        } else if (!mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {//不會進入此分支
						...
			        }
			        mStartActivity.setTask(sourceTask, null);//設定目標Activity B的Task為A Activity所屬的Task

			        return START_SUCCESS;
			    }				
			/*****************************************************************************/						            
            if (result != START_SUCCESS) {
                return result;
            }
        } else if (mInTask != null) {//啟動時指定了目標堆疊(mInTask),ActivityRecord系結到mInTask,此場景下不會進入此分支
			...
        } else {//不會進入此分支,忽略
			..,
        }		
		...
		/*把當前啟動的Activity加入TaskRecord以及系結WindowManagerService*/
        mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);	
		/*****************************************************************************/	
			//這里小伙們就不要關注排版問題了,主要是為了演示整個流程,各位就將就一下  
		    final void startActivityLocked(	ActivityRecord r, //此時的r為目標Activity
		    								boolean newTask, //newTask表示是否要創建Task,為true
		    								boolean keepCurTransition,
		            						ActivityOptions options) 
		   {
		        TaskRecord rTask = r.task;
		        final int taskId = rTask.taskId;
				
		        if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {//不會進入此分支
					...
		        }
		        TaskRecord task = null;
		        if (!newTask) {//newTask為false會走入此分支
					...
		        }
		
				...
		
		        task = r.task;
		
				//將Activity移動到Stack的頂端
		        task.addActivityToTop(r);
		        task.setFrontOfTask();
		
		        r.putInHistory();
		        if (!isHomeStack() || numActivities() > 0) {//會進入此分支,此時的ActivityStack不是HomeStack
					//這個地方很重要
		            addConfigOverride(r, task);
		        } else {//不會進入此分支
					...
		        }
		        ...
		    }	
		/*****************************************************************************/		
		...	 
}		   		              

至此從從已經啟動應用的A Activity跳轉到B Activity的Task以及Stack的調度就完成了,我們對其小結一下,其主要流程可以精簡為如下幾個步驟:

  • 呼叫AS.setInitialState方法,對啟動目標Activity時Task任務堆疊和ActivityStack堆疊將要涉及的變數進行初始化設定
  • 根據發起端Activity的情況,呼叫computeLaunchingTaskFlags計算目標Activity的launchMode模式
  • 呼叫computeSourceStack確定發起端Stack的情況
  • 經過上述的一系列處理以后,已經確定目標Activity相關的LauncherMode,flag等啟動模式了,接下來呼叫getReusableIntentActivity確定是否有可復用的Activity,在此場景下肯定不會存在可以復用的Activity
  • 接著呼叫方法setTaskFromSourceRecord從發起端Activity獲取Task和Stack
  • 接著繼續呼叫ASS.startActivityLocked方法,將目標Activity加入TaskRecord,并且將其放入對應ActivityStack的頂部以及系結WindowManagerService

經過如上的步驟以后,我們的目標Activity所屬的Task和Stack就已經安排妥當了,我們此時可以通過命令查看,可以看到此時的mFocusedStack和Task為目標Activity A所對應的,

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

    Running activities (most recent first):
      TaskRecord{d361623 #9 A=com.example.test U=0 StackId=1 sz=2}
        Run #1: ActivityRecord{59a829e u0 com.example.test/.BActivity t9}
        Run #0: ActivityRecord{dd0bb9a u0 com.example.test/.AMainActivity t9}

    mResumedActivity: ActivityRecord{59a829e u0 com.example.test/.BActivity t9}
    mLastPausedActivity: ActivityRecord{dd0bb9a u0 com.example.test/.AMainActivity t9}

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

    Running activities (most recent first):
      TaskRecord{3a6c4af #8 A=com.android.launcher3 U=0 StackId=0 sz=1}
        Run #0: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8}

    mLastPausedActivity: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8}

  mFocusedActivity: ActivityRecord{59a829e u0 com.example.test/.BActivity t9}//焦點Activity為BActivity
  mFocusedStack=ActivityStack{8238d9 stackId=1, 1 tasks} mLastFocusedStack=ActivityStack{8238d9 stackId=1, 1 tasks}
  mSleepTimeout=false
  mCurTaskIdForUser={0=9}
  mUserStackInFront={}
  mActivityContainers={0=ActivtyContainer{0}A, 1=ActivtyContainer{1}A}
  mLockTaskModeState=NONE mLockTaskPackages (userId:packages)=
    0:[]
 mLockTaskModeTasks[]


1.3 按Home按鍵退出應用,然后重新打開應用

??我們先不除錯,我們想想想此時場景下會怎么處理呢!此時的Task相關的調度邏輯如下,此時會先找到已有的相關task,并顯示堆疊頂的Activity,任務調度執行如下所示:

//[ActivityStarter.java]
	/*
	這里的sourceRecord是指發起呼叫者 
	r是指本次的將要啟動的Activity
	startFlags取值為0
	doResume的值為true
	inTask為發起方指定的任務堆疊,此時為null
	這里主要確定目標Activity的launchMode Task堆疊等,即Task的創建和管理
	*/
    private int startActivityUnchecked(final ActivityRecord r, 
    									ActivityRecord sourceRecord,
            							IVoiceInteractionSession voiceSession, 
            							IVoiceInteractor voiceInteractor,
            							int startFlags, 
            							boolean doResume, 
            							ActivityOptions options, 
            							TaskRecord inTask) {

		//設定初始化狀態,此時需要重點關注該方法中的如下幾個目標值的獲取,為了分析的方便,我會將該方法展開
		setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
		              voiceInteractor);
        /*****************************************************************************/
        	//這里小伙們就不要關注排版問題了,主要是為了演示整個流程,各位就將就一下
			private void setInitialState(ActivityRecord r, 
										ActivityOptions options, 
										TaskRecord inTask,
            							boolean doResume, 
            							int startFlags, 
            							ActivityRecord sourceRecord,
            							IVoiceInteractionSession 
            							voiceSession, 
            							IVoiceInteractor voiceInteractor) {
						//此處分支會走,將啟動程序中的涉及的相關變數進行初始化
						reset();     
						...   
						mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP;//此時為false
						mLaunchSingleInstance = r.launchMode == LAUNCH_SINGLE_INSTANCE;//此時為false
						mLaunchSingleTask = r.launchMode == LAUNCH_SINGLE_TASK;//此時為false
						//這里重點關注一下,此時mIntent.getFlags攜帶的flags為0,此處很關鍵
						mLaunchFlags = adjustLaunchFlagsToDocumentMode(r,mLaunchSingleInstance, mLaunchSingleTask, mIntent.getFlags());
						...
						mDoResume = doResume;
						...
						mInTask = inTask;
						...
						mStartFlags = startFlags;//此時的mStartFlags為0
      		}
        /*****************************************************************************/
		//根據發起端,計算目標Activity的launchMode模式
		computeLaunchingTaskFlags();
		/*****************************************************************************/
			//這里小伙們就不要關注排版問題了,主要是為了演示整個流程,各位就將就一下
			//根據發起者launchMode情況,決定目標Activity的Flags情況
    		private void computeLaunchingTaskFlags() {
    			//此時場景下的mSourceRecord不為null,mInTask為null,所以不會進入該分支
    			if (mSourceRecord == null && mInTask != null && mInTask.stack != null) {
    				...
    			}else {//會進入此分支
		            mInTask = null;
		            //不滿足條件,不會進入該分支
		            if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null
		                    && mSourceRecord.isFreeform())  {
		                	mAddingToTask = true;
		            }
    			}
        	}
        	//此時的mInTask為null,會進入此分支,但是該分支下面的三個小分支都不會進入
        	if (mInTask == null) {
	            if (mSourceRecord == null) {//mSourceRecord不為null,此時的mSourceRecord為Launcher,不會進入此分支
					...
	            } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {//launcher應用activity的啟動模式為singleTask
					...
	            } else if (mLaunchSingleInstance || mLaunchSingleTask) {//不會進入此分支,此時都為false
					...
	            }
        	}
		/*****************************************************************************/    
		//確定發起端的ActivityStack情況
		computeSourceStack();
		/*****************************************************************************/
			//這里小伙們就不要關注排版問題了,主要是為了演示整個流程,各位就將就一下     
			//確定發起端的Stack情況
	    	private void computeSourceStack() {
		        if (mSourceRecord == null) {//mSourceRecord不為null,不會進入此分支
					...
		        }
		        if (!mSourceRecord.finishing) {//此時明顯mSourceRecord沒有被finish所以會進入此分支
					//當呼叫者Activity不為空,且不處于finishing狀態,則其所在堆疊賦于sourceStack
		            mSourceStack = mSourceRecord.task.stack;
		            return;
		        }
				...
	    	}   
   	  /*****************************************************************************/	

		mIntent.setFlags(mLaunchFlags);//設定目標Activity的launchMode啟動模式

		 // 根據mLaunchFlags來查找是否有可復用的activity
		 /**
	      * 這邊主要是判斷當前啟動的Activity是否存在可以利用的Task
	      * 當啟動模式launchMode為singleTask、singleInstance,或者啟動時
	      * Flag設定為FLAG_ACTIVITY_NEW_TASK并沒設定FLAG_ACTIVITY_MULTIPLE_TASK
	      * 并且當前啟動的Activity不是以startActivityForResult啟動的,
	      * 滿足以上情況才會尋找是否存在有復用的Task,
	      * 匹配規則:
	      * 1、對于啟動模式為singleInstance,遍歷所有ActivityStack和Task的堆疊中查找
	      *是否存在以當前啟動Activity相同的Activity,
	      * 2、其它情況下,遍歷所有ActivityStack和Task的堆疊,查找Task中intent變數                                                      * 是否當前啟動Activity相匹配,如果不存在,則去匹配task的親和性(即
	  	   *在AndroidManifest中android:taskAffinity定義的,
	      */
		 //此時存在復用額Activity,因為我們前面已經創建了目標Activity而且沒有被銷毀
		 mReusedActivity = getReusableIntentActivity();
		/*****************************************************************************/	
			//這里小伙們就不要關注排版問題了,主要是為了演示整個流程,各位就將就一下 	 
			 private ActivityRecord getReusableIntentActivity() {
			 	  //此時的mLaunchFlags的取值為FLAG_ACTIVITY_RESET_TASK_IF_NEEDED|FLAG_ACTIVITY_NEW_TASK,所以為true
			      boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
			              (mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
			              || mLaunchSingleInstance || mLaunchSingleTask;
			
				  //此時mInTask為false,mStartActivity.resultTo為null
			      putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
			      ActivityRecord intentActivity = null;
			      if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
			      	...
			      } else if (putIntoExistingTask) {
			          if (mLaunchSingleInstance) {//不會進入此分支
					...
			          } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {//不會進入此分支
					...
			          } else {//會走入此分支,在ASS中查找是否存在啟動目標Activity,很明顯此時存在,所以intentActivity得到的值為Activity B
			              intentActivity = mSupervisor.findTaskLocked(mStartActivity);
			          }
			      }
			      return intentActivity;
			  }
	    /*****************************************************************************/	

		 //如果找到了可重用的activity,需要清理掉原來的資訊,并把當前啟動的activity的資訊拷貝進去
		 //做清理和拷貝作業,此時會進入此分支
        if (mReusedActivity != null) {	
        	...
			//設定當前啟動Activity的Task為復用的Task,進入此分支
            if (mStartActivity.task == null) {
                mStartActivity.task = mReusedActivity.task;
            }        

	           /*
			 *這邊處理啟動時設定FLAG_ACTIVITY_CLEAR_TOP時,要清除復用Task中存在與當前啟動
			 *Activity相同的Activity之上的Activity
			 *舉個例子:比如復用Task1中存在有Activity A,B,C,D,此時正在啟動的Activity B,那么C**和D也要finish,另外此時如果B *為標準啟動模式,并且沒有設定FLAG_ACTIVITY_SINGLE_TOP,那么B也會finish,具體的讀者可以跟進
			 *mReusedActivity.task.performClearTaskForReuseLocked看下,
			 */
	           if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
	                   || mLaunchSingleInstance || mLaunchSingleTask) {//不會進入此分支
				...
	           }
			// 計算哪個task和activity要移至前臺,必要時會進行task的清理作業
	        mReusedActivity = setTargetStackAndMoveToFrontIfNeeded(mReusedActivity);
			/*****************************************************************************/	
				//這里小伙們就不要關注排版問題了,主要是為了演示整個流程,各位就將就一下 	  
			    private ActivityRecord setTargetStackAndMoveToFrontIfNeeded(ActivityRecord intentActivity) {
			        mTargetStack = intentActivity.task.stack;
			        mTargetStack.mLastPausedActivity = null;
			        final ActivityStack focusStack = mSupervisor.getFocusedStack();
			        ActivityRecord curTop = (focusStack == null)
			                ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);//獲取當前前臺ActivityStack堆疊頂的ActivityRecord		
					//判斷頂部的堆疊是否符合要求(即判斷現在堆疊頂的堆疊是否為能夠復用的activityrecord所在的堆疊)
					if (curTop != null
			                && (curTop.task != intentActivity.task || curTop.task != focusStack.topTask())
			                && !mAvoidMoveToFront) {
			            //增加一個標記,標識這個task是從任務堆疊的后面移動上來的
			            mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
			
						//這里的mSourceRecord表示的是發起端,此處是判斷合法性
			            if (mSourceRecord == null || (mSourceStack.topActivity() != null &&
			                    mSourceStack.topActivity().task == mSourceRecord.task)) {//次場景會進入此分支
			                if (mLaunchTaskBehind && mSourceRecord != null) {//此時的mLaunchTaskBehind為null不進入此分支
			                    ...
			                }
			                mMovedOtherTask = true;
			
	
			                final boolean willClearTask =
			                        (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
			                            == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);//此時willClearTask 的取值為false
			                if (!willClearTask) {//不需要清空,那么就需要將復用的task移至堆疊頂
		
								//根據規則獲取當前要啟動activity所屬的ActivityStack堆疊
			                    final ActivityStack launchStack = getLaunchStack(
			                            mStartActivity, mLaunchFlags, mStartActivity.task, mOptions);
			                     //當要啟動的堆疊與目標一致或者要啟動的堆疊為空,這是我們一般的標準流程,會呼叫moveTaskToFrontLocked方法,將當前堆疊移動到與用戶互動的堆疊頂
			                    //此時會進入該分支
			                    if (launchStack == null || launchStack == mTargetStack) {
			                        mTargetStack.moveTaskToFrontLocked(
			                                intentActivity.task, mNoAnimation, mOptions,
			                                mStartActivity.appTimeTracker, "bringingFoundTaskToFront");
			                        mMovedToFront = true;
			                    } else if (launchStack.mStackId == DOCKED_STACK_ID
			                            || launchStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {//不會進入該分支
			                            ...
			                    }
			                    mOptions = null;
			                }
			                updateTaskReturnToType(intentActivity.task, mLaunchFlags, focusStack);
			            }
			        }
			        if (!mMovedToFront && mDoResume) {//不進入此分支
						...
			        }
			
			        mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.task, INVALID_STACK_ID,
			                mTargetStack.mStackId);
			
					//此時Luancher啟動的時候攜帶了該值,所以會走入此分支
			        if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
			            return mTargetStack.resetTaskIfNeededLocked(intentActivity, mStartActivity);
			        }
				...
			    }			
			/*****************************************************************************/		
            setTaskFromIntentActivity(mReusedActivity);

            if (!mAddingToTask && mReuseTask == null) {//進入此分支

                resumeTargetStackIfNeeded();
				Log.e(ACTIVITY_TAG, "mReusedActivity != null START_TASK_TO_FRONT");
                return START_TASK_TO_FRONT;
            }

       }				           
}

至此從按Home按鍵退出應用,然后重新打開應用的Task以及Stack的調度就完成了,我們對其小結一下,其主要流程可以精簡為如下幾個步驟:

  • 呼叫AS.setInitialState方法,對啟動目標Activity時Task任務堆疊和ActivityStack堆疊將要涉及的變數進行初始化設定
  • 根據發起端Activity的情況,呼叫computeLaunchingTaskFlags計算目標Activity的launchMode模式
  • 呼叫computeSourceStack確定發起端Stack的情況
  • 經過上述的一系列處理以后,已經確定目標Activity相關的LauncherMode,flag等啟動模式了,接下來呼叫getReusableIntentActivity確定是否有可復用的Activity,在此場景下存在復用的Activity
  • 進入存在復用Activity的分支,呼叫setTargetStackAndMoveToFrontIfNeeded處理復用的Activity和Task以及Stack
  • 接著直接呼叫resumeTargetStackIfNeeded恢復復用Task任務堆疊

經過如上的步驟以后,我們的目標Activity所屬的Task和Stack就已經安排妥當了,我們此時可以通過命令查看相對應的Task任務堆疊和Stack堆疊了,

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

    Running activities (most recent first):
      TaskRecord{d361623 #9 A=com.example.test U=0 StackId=1 sz=2}
        Run #1: ActivityRecord{59a829e u0 com.example.test/.BActivity t9}
        Run #0: ActivityRecord{dd0bb9a u0 com.example.test/.MainActivity t9}

    mResumedActivity: ActivityRecord{59a829e u0 com.example.test/.BActivity t9}

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

    Running activities (most recent first):
      TaskRecord{3a6c4af #8 A=com.android.launcher3 U=0 StackId=0 sz=1}
        Run #0: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8}

    mLastPausedActivity: ActivityRecord{d5e92f9 u0 com.android.launcher3/.Launcher t8}

  mFocusedActivity: ActivityRecord{59a829e u0 com.example.test/.BActivity t9}
  mFocusedStack=ActivityStack{8238d9 stackId=1, 1 tasks} mLastFocusedStack=ActivityStack{8238d9 stackId=1, 1 tasks}
  mSleepTimeout=false
  mCurTaskIdForUser={0=9}
  mUserStackInFront={}
  mActivityContainers={0=ActivtyContainer{0}A, 1=ActivtyContainer{1}A}
  mLockTaskModeState=NONE mLockTaskPackages (userId:packages)=
    0:[]
 mLockTaskModeTasks[]



總結

??Activity啟動流程(三)- Activity Task調度演算法復盤分析到這里就要告一段落了,從前面的分析可以看出來,Activity和Task的調度演算法非常復雜,最好需結合實際場景才好分析,只有這樣才知道是否需要新建Task,還是將新的ActivityRecord加入到已有的Task里,當前這一切的前提條件是我們能理解啟動模式的一些特點,這樣才能對理解調度演算法有一個基礎,如果一上來就是懵懵懂懂的亂干那就完蛋了,因為你會被這原始碼繞的暈頭轉向的搞不清方向了,好了今天就到這里了,希望小伙們能點贊和關注,謝謝!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/170670.html

標籤:其他

上一篇:CCF 202009-1 稱檢測點查詢

下一篇:12行代碼AC_Justifying the Conjecture Gym - 102394J(解題報告)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more