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

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

2020-09-29 04:29:41 前端設計

??????Activity啟動流程原始碼實作詳解(二)

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

Android應用行程創建流程大揭秘
Android四大組件之bindService原始碼實作詳解
Android四大組件之Activity啟動流程原始碼實作詳解概要
Android四大組件之Activity啟動流程原始碼實作詳解(一)
Android四大組件之Activity啟動流程原始碼實作詳解(二)


前言

??還記得大明湖畔的夏雨荷嗎!錯了,還記得我們前面章節博客Android四大組件之Activity啟動流程原始碼實作詳解(一)嗎,在上述博客中我們重點分析了Activity啟動的如下相關知識點:

  • 發起端行程是怎么通過Instrumentation管理類并且借助AMP完成啟動Activity請求的發送
  • system_server行程中的AMS初步處理啟動Activiyt的請求,并借助PKMS服務決議intent獲取目標Activity的ActivityInfo資訊,然后通過上述決議得到的資料為目標Activiyt構建ActivityRecord資料結構

那么在本篇博客中我們將繼續分析system_server對Activity啟動請求的處理流程:

  • system_server行程通過AMS處理啟動Activity請求
    ?4.為目標Activity查找/分配或者創建最優Task堆疊以及ActivityStack堆疊
    ?5.Pause前臺Activity
    ?6.Resume請求的目標Activity
    ?7.AMS請求zygote行程為目標Activity創建所屬行程

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

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

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

frameworks/base/core/java/android/app/
  --- IActivityManager.java
  --- ActivityManagerNative.java (內部包含AMP)
  --- ActivityManager.java
  --- AppGlobals.java
  --- Activity.java
  --- ActivityThread.java(內含AT)
  --- LoadedApk.java  
  --- AppGlobals.java
  --- Application.java
  --- Instrumentation.java
  
  --- IApplicationThread.java
  --- ApplicationThreadNative.java (內部包含ATP)
  --- ActivityThread.java (內含ApplicationThread)
  --- ContextImpl.java
  • 并且在后續的原始碼分析程序中為了簡述方便,會將做如下簡述:
    ApplicationThreadProxy簡稱為ATP
    ActivityManagerProxy簡稱為AMP
    ActivityManagerService簡稱為AMS
    ActivityManagerNative簡稱AMN
    ApplicationThreadNative簡稱ATN
    PackageManagerService簡稱為PKMS
    ApplicationThread簡稱為AT
    ActivityStarter簡稱為AS,這里不要和ActivityServices搞混淆了
    ActivityStackSupervisor簡稱為ASS

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

在這里插入圖片描述



一.前期知識準備

??說實話,此處章節是我在將第二大章節寫了一大半情況下回過頭來撰寫的,因為如果這里不將此處的知識點闡述清楚的話,感覺后面的章節沒有辦法進行!還記得我們在前面博客Android四大組件之Activity啟動流程原始碼實作詳解概要的章節2.7中我們分析了ActivityRecord、TaskRecord、ActivityStack、ProcessRecord、ActivityStackSupervisor在整個Activity啟動的中的關系視圖,但是該示意圖是從整個AMS框架來說的,并且也只是介紹了一個大概,
在這里插入圖片描述

今天我們先拋開AMS的整體不談,先談談ActivityRecord、TaskRecord、ActivityStack這三個類之間的關聯,因為本篇的原始碼分析將會重點涉及到這三者,所以要先弄明白幾個重要的類及概念(ActivityRecord在前面的博客Android四大組件之Activity啟動流程原始碼實作詳解(一)已經有分析過了),所以在正式原始碼前我們先來看看另外兩者!
在這里插入圖片描述


1.1 TaskRecord

??TaskRecord是用來管理ActivityRecord的容器資料結構,用于記錄該任務堆疊的activity開啟的先后順序,而TaskRecord對這些ActivityRecord的管理是以堆疊的形式來管理的,既然是堆疊那就肯定滿足后進先出的原則!TaskRecord管理的ActivityRecord不一定都屬于同一個App行程,這個需要根據實際情況來判定(這個從我們的最上面的示意圖也可以看到)!

//[TaskRecord.java]
final class TaskRecord {
	final int taskId;       // 任務ID
    String affinity;        // 是指root activity的affinity,即該Task中第一個Activity
    String rootAffinity;    // 
	Intent intent;          // 最開始創建該Task的intent

	int userId;             // 記錄創建該Task的用戶id

    
    final ArrayList<ActivityRecord> mActivities;//使用一個ArrayList來保存所有的管理的ActivityRecord

    String mCallingPackage;//呼叫者包名
    ActivityStack stack;//TaskRecord所在的ActivityStack
	final ActivityManagerService mService;//AMS的

	//添加Activity到Task堆疊頂部
    void addActivityToTop(ActivityRecord r) {
        addActivityAtIndex(mActivities.size(), r);
    }

	//添加Activity到指定的索引位置
    void addActivityAtIndex(int index, ActivityRecord r) {
    	...
    }

	//獲取根ActivityRecord的intent資訊
    Intent getBaseIntent() {
        return intent != null ? intent : affinityIntent;
    }
    
    //獲取根ActivityRecord,即Task堆疊底的ActivityRecord(因為它是后進先出)
    ActivityRecord getRootActivity() {
        for (int i = 0; i < mActivities.size(); i++) {
            final ActivityRecord r = mActivities.get(i);
            if (r.finishing) {
                continue;
            }
            return r;
        }
        return null;
    }
	//獲取堆疊頂ActivityRecord
    ActivityRecord getTopActivity() {
        for (int i = mActivities.size() - 1; i >= 0; --i) {
            final ActivityRecord r = mActivities.get(i);
            if (r.finishing) {
                continue;
            }
            return r;
        }
        return null;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(128);
        if (stringName != null) {
            sb.append(stringName);
            sb.append(" U=");
            sb.append(userId);
            sb.append(" StackId=");
            sb.append(stack != null ? stack.mStackId : INVALID_STACK_ID);
            sb.append(" sz=");
            sb.append(mActivities.size());
            sb.append('}');
            return sb.toString();
        }
        sb.append("TaskRecord{");
        sb.append(Integer.toHexString(System.identityHashCode(this)));
        sb.append(" #");
        sb.append(taskId);
        if (affinity != null) {
            sb.append(" A=");
            sb.append(affinity);
        } else if (intent != null) {
            sb.append(" I=");
            sb.append(intent.getComponent().flattenToShortString());
        } else if (affinityIntent != null) {
            sb.append(" aI=");
            sb.append(affinityIntent.getComponent().flattenToShortString());
        } else {
            sb.append(" ??");
        }
        stringName = sb.toString();
        return toString();
    }
}

有了前面的分析,我們來實際看看TaskRecord的資料情況,而恰好Android為我們提供了一個很好的命令dumpsy可以讓我們洞察一切,通過dump可以看到此時存在有八個TaskRecord!

XXX:/ # dumpsys activity | grep TaskRecord
  * Recent #0: TaskRecord{b80c4da #80 A=com.android.launcher3 U=0 StackId=0 sz=1}
  * Recent #1: TaskRecord{c6a6d0b #1 I=com.android.settings/.FallbackHome U=0 StackId=-1 sz=0}
  * Recent #2: TaskRecord{80bf7e8 #79 A=com.android.settings U=0 StackId=-1 sz=0}
  * Recent #3: TaskRecord{8116e01 #77 A=com.android.calculator2 U=0 StackId=-1 sz=0}
  * Recent #4: TaskRecord{4b654a6 #76 A=com.cyanogenmod.filemanager U=0 StackId=-1 sz=0}
  * Recent #5: TaskRecord{dd0cee7 #75 A=org.codeaurora.gallery U=0 StackId=-1 sz=0}
  * Recent #6: TaskRecord{e9caa94 #74 A=com.xxx.printtest U=0 StackId=-1 sz=0}
  * Recent #7: TaskRecord{725e93d #44 I=com.android.settings/.Settings$DataUsageSummaryActivity U=0 StackId=-1 sz=0}
      TaskRecord{b80c4da #80 A=com.android.launcher3 U=0 StackId=0 sz=1}
      TaskRecord{b80c4da #80 A=com.android.launcher3 U=0 StackId=0 sz=1}

1.2 ActivityStack

??如果說TaskRecord是ActivityRecord的大管家,那么ActivityStack則是TaskRecord大管家,即ActivityStack是用來管理TaskRecord一種結構容器,ActivityStack也是我們通常意義上所說的Activity堆疊,它存在如下的幾種的StackId(即我們可以把ActivityStack歸納為幾種情況),這個也可以通過前面1.1章節最后的列印看出來!

//【ActivityManager.java StackId]
    public static class StackId {

        public static final int INVALID_STACK_ID = -1;//非法stack ID.
        public static final int FIRST_STATIC_STACK_ID = 0;
        
        //Launcher的Activity以及recentsAPP
        public static final int HOME_STACK_ID = FIRST_STATIC_STACK_ID;
        
        //正常啟動Activity的所處的ActivityStack
        public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
        
        public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;
        public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;//這個是分屏應用所處于的ActivityStack
        public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;//畫中畫模式   
        public static final int LAST_STATIC_STACK_ID = PINNED_STACK_ID;  
        public static final int FIRST_DYNAMIC_STACK_ID = LAST_STATIC_STACK_ID + 1;
}

接著繼續修理ActivityStack,把我的意大利炮拿出來朝著它狠狠的開炮!

//
final class ActivityStack {
         ...
         
         //Activity狀態值,和Activity生命周期對應
    enum ActivityState {
        INITIALIZING,
        RESUMED,
        PAUSING,
        PAUSED,
        STOPPING,
        STOPPED,
        FINISHING,
        DESTROYING,
        DESTROYED
    }
    
 	...
    final ActivityManagerService mService;//AMS服務的參考
    final WindowManagerService mWindowManager;//WMS服務的參考

 
	//使用一個ArrayList來保存TaskRecord
    private final ArrayList<TaskRecord>mTaskHistory = new ArrayList<>();
 
 	//該Stack中正在pause的ActivityRecord
	ActivityRecord mPausingActivity = null;

	//個表示當前ActivityStack處于已經resumed的activity
    ActivityRecord mResumedActivity = null;
 

    //表示該Stack中最后被paused程序的activity
    ActivityRecord mLastPausedActivity = null;
 
	
    ActivityRecord mLastStartedActivity = null;
 
    final int mStackId;//該Stack的身份ID
     
     ...
    //在每個ActivityStack中保存著一份所有的ActivityStack
    ArrayList<ActivityStack> mStacks;
    
    int mDisplayId;
 
    
    //管理ActivityStack的的ASS的參考
    final ActivityStackSupervisor mStackSupervisor;

	//創建TakRecord,這個會在后面的博客中呼叫到
    TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            boolean toTop) {

		//創建一個task
        TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession,
                voiceInteractor);
        // add the task to stack first, mTaskPositioner might need the stack association
		//將task添加到ActivityStack中去
		addTask(task, toTop, "createTaskRecord");
        final boolean isLockscreenShown = mService.mLockScreenShown == LOCK_SCREEN_SHOWN;
        if (!layoutTaskInStack(task, info.windowLayout) && mBounds != null && task.isResizeable()
                && !isLockscreenShown) {
            task.updateOverrideConfiguration(mBounds);
        }
        return task;
    }
	//添加TaskRecord
    void addTask(final TaskRecord task, final boolean toTop, String reason) {
        final ActivityStack prevStack = preAddTask(task, reason, toTop);

        task.stack = this;
        if (toTop) {
            insertTaskAtTop(task, null);
        } else {
            mTaskHistory.add(0, task);
            updateTaskMovement(task, false);
        }
        postAddTask(task, prevStack);
    }
    @Override
    public String toString() {
        return "ActivityStack{" + Integer.toHexString(System.identityHashCode(this))
                + " stackId=" + mStackId + ", " + mTaskHistory.size() + " tasks}";
    }
    ..
}

??通過前面的原始碼我們可以看到ActivityStack使用了一個ArrayList來保存TaskRecord,另外,ActivityStack中還持有ActivityStackSupervisor物件的參考,這個是用來管理ActivityStacks的這里就不過多介紹了,章節1.3會簡單介紹一下,后面會開辟一個專門的章節來分析這塊!

有了前面的分析,我們來實際看看ActivityStack的資料情況,而恰好Android為我們提供了一個很好的命令dumpsy可以讓我們洞察一切,通過dump可以看到此時存在一個ActivityStack!

XXX:/ # dumpsys activity | grep ActivityStack
  mFocusedStack=ActivityStack{55a06c8 stackId=1, 5 tasks} mLastFocusedStack=ActivityStack{55a06c8 stackId=1, 5 tasks}

1.3 ActivityStackSupervisor

??如果說ActivityStack則是TaskRecord大管家,那么ActivityStackSupervisor則是ActivityStack大管家,即ActivityStackSupervisor是用來管理ActivityStack一種結構容器,

public final class ActivityStackSupervisor implements DisplayListener {
	...
	final ActivityManagerService mService;//AMS 實體物件的參考
	private RecentTasks mRecentTasks;//管理RecentTasks 
	int mCurrentUser;//當前用戶
	ActivityStack mHomeStack;//Home所屬Stack
	ActivityStack mFocusedStack;//當前持有焦點的Stack
	private ActivityStack mLastFocusedStack;//最后獲取焦點的stack,此時表示正在切換
	//ActivityDisplay串列,以當前的displayid為key這個可以對應多種顯示設備,我們這里只考慮一種),以ActivityDisplay為value
	private final SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<>();
	private SparseArray<ActivityContainer> mActivityContainers = new SparseArray<>();//以mStackId為key



    class ActivityDisplay {
        int mDisplayId;
        Display mDisplay;
        DisplayInfo mDisplayInfo = new DisplayInfo();


        final ArrayList<ActivityStack> mStacks = new ArrayList<>();

        ActivityRecord mVisibleBehindActivity;

        ActivityDisplay() {
        }
        ...
    }
	...
}

這里我們重點關注一下mHomeStack和mFocusedStack以及mLastFocusedStack,并且mActivityDisplays是通過displayid來區分當前顯示的ActivityStack的,


1.4 Activity各種堆疊關系總結

在這里插入圖片描述
??有了前面知識的鋪墊,我們這里對AMS中牽涉的各種堆疊結構體的組成關系來簡單總結一下:

  • 在不考慮分屏和虛擬屏的情況下,我們認為ActivityStackSupervisor與ActivityDisplay都是系統唯一,如果考慮起來就復雜了
  • 每個ActivityStack中可以有若干個TaskRecord物件,它有一個ArrayList型別的成員mTaskHistory,用于存盤TaskRecord
  • 每個TaskRecord包含如果若干個ActivityRecord物件,這就是我們常說的任務堆疊,具有后進先出的特點
  • 每個ActivityRecord記錄一個Activity資訊,并且每個ActivityRecord會對應到一個TaskRecord,ActivityRecord中型別為TaskRecord的成員task,記錄所屬的Task,這里有一點需要注意的是Activity和ActivityRecord并不是一對一的,而是一對多,因為一個Actitiy可能存在多個啟動方式進而導致存在多個ActivityRecord
  • ActivityStackSupervisor,負責所有Activity堆疊的管理,內部管理了mHomeStack、mFocusedStack和mLastFocusedStack三個Activity堆疊,其中,mHomeStack管理的是Launcher相關的Activity堆疊;mFocusedStack管理的是當前顯示在前臺Activity的Activity堆疊;mLastFocusedStack管理的是上一次顯示在前臺Activity的Activity堆疊
  • ActivityDisplay主要有Home Stack和App Stack這兩個堆疊

上述幾個之間的管理非常緊湊,可以通過正向鏈表和反向鏈表通過其中的一個點切入獲取到其它對應的關聯的結構,這個從后續分析的查找復用ActivityRecord和Task以及Stack可以看出來

  • 正向關系鏈表
ActivityStackSupervisor.mActivityDisplays
---> ActivityDisplay.mStacks
---> ActivityStack.mTaskHistory
---> TaskRecord.mActivities
---> ActivityRecord
  • 反向關系鏈表
ActivityRecord.task
---> TaskRecord.stack
---> ActivityStack.mStackSupervisor
---> ActivityStackSupervisor

關于上述之間的關聯,小伙們可以使用命令dumpsys activity activities進行查看,雖然這里將貼出來會顯得很啰嗦,但是為了小伙們的學習我這里也是拼了,因為這樣會更加的清晰明了!

ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
Display #0 (activities from top to bottom)://測驗終端只存在一個顯示設備,所以Displayid只會存在一個
  Stack #1://當前焦點的ActivtyStack id
  mFullscreen=true
  mBounds=null
    Task id #96//當前獲取焦點的Task任務堆疊id
    mFullscreen=true
    mBounds=null
    mMinWidth=-1
    mMinHeight=-1
    mLastNonFullscreenBounds=null
    * TaskRecord{56df812 #96 A=com.cyanogenmod.filemanager U=0 StackId=1 sz=1}//Task任務堆疊具體資訊
      userId=0 effectiveUid=u0a21 mCallingUid=u0a23 mUserSetupComplete=true mCallingPackage=com.android.launcher3
      affinity=com.cyanogenmod.filemanager
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.cyanogenmod.filemanager/.activities.NavigationActivity}//啟動目標Activity的intent資訊
      realActivity=com.cyanogenmod.filemanager/.activities.NavigationActivity
      autoRemoveRecents=false isPersistable=true numFullscreen=1 taskType=0 mTaskToReturnTo=1
      rootWasReset=true mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
      Activities=[ActivityRecord{dac4005 u0 com.cyanogenmod.filemanager/.activities.NavigationActivity t96}]//當前獲取焦點的Activity的ActivityRecord資訊
      askedCompatMode=false inRecents=true isAvailable=true
      lastThumbnail=null lastThumbnailFile=/data/system_ce/0/recent_images/96_task_thumbnail.png
      stackId=1
      hasBeenVisible=true mResizeMode=RESIZE_MODE_UNRESIZEABLE isResizeable=false firstActiveTime=1601263245356 lastActiveTime=1601263245356 (inactive for 38s)
      * Hist #0: ActivityRecord{dac4005 u0 com.cyanogenmod.filemanager/.activities.NavigationActivity t96}
          packageName=com.cyanogenmod.filemanager processName=com.cyanogenmod.filemanager
          launchedFromUid=10023 launchedFromPackage=com.android.launcher3 userId=0
          app=ProcessRecord{1544be3 8312:com.cyanogenmod.filemanager/u0a21}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.cyanogenmod.filemanager/.activities.NavigationActivity bnds=[536,168][712,356] }
          frontOfTask=true task=TaskRecord{56df812 #96 A=com.cyanogenmod.filemanager U=0 StackId=1 sz=1}
          taskAffinity=com.cyanogenmod.filemanager
          realActivity=com.cyanogenmod.filemanager/.activities.NavigationActivity
          baseDir=/system/app/CMFileManager/CMFileManager.apk
          dataDir=/data/user/0/com.cyanogenmod.filemanager
          stateNotNeeded=false componentSpecified=true mActivityType=0
          compat={320dpi} labelRes=0x7f0c0008 icon=0x7f020062 theme=0x7f0e0000
          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=ff1e88e5
          launchFailed=false launchCount=1 lastLaunchTime=-38s806ms
          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=1
          frozenBeforeDestroy=false forceNewConfig=false
          mActivityType=APPLICATION_ACTIVITY_TYPE
          waitingVisible=false nowVisible=true lastVisibleTime=-37s955ms
          resizeMode=RESIZE_MODE_UNRESIZEABLE
    Task id #95//Stack  id為1中另外的Taask
    mFullscreen=true
    mBounds=null
    mMinWidth=-1
    mMinHeight=-1
    mLastNonFullscreenBounds=null
    * TaskRecord{495a9e0 #95 A=org.codeaurora.gallery U=0 StackId=1 sz=1}
      userId=0 effectiveUid=u0a13 mCallingUid=u0a23 mUserSetupComplete=true mCallingPackage=com.android.launcher3
      affinity=org.codeaurora.gallery
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=org.codeaurora.gallery/com.android.gallery3d.app.GalleryActivity}
      realActivity=org.codeaurora.gallery/com.android.gallery3d.app.GalleryActivity
      autoRemoveRecents=false isPersistable=true numFullscreen=1 taskType=0 mTaskToReturnTo=1
      rootWasReset=true mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
      Activities=[ActivityRecord{1c174fa u0 org.codeaurora.gallery/com.android.gallery3d.app.GalleryActivity t95}]
      askedCompatMode=false inRecents=true isAvailable=true
      lastThumbnail=android.graphics.Bitmap@bbc499 lastThumbnailFile=/data/system_ce/0/recent_images/95_task_thumbnail.png
      stackId=1
      hasBeenVisible=true mResizeMode=RESIZE_MODE_FORCE_RESIZEABLE isResizeable=true firstActiveTime=1601263244132 lastActiveTime=1601263244132 (inactive for 40s)
      * Hist #0: ActivityRecord{1c174fa u0 org.codeaurora.gallery/com.android.gallery3d.app.GalleryActivity t95}
          packageName=org.codeaurora.gallery processName=org.codeaurora.gallery
          launchedFromUid=10023 launchedFromPackage=com.android.launcher3 userId=0
          app=ProcessRecord{ff4ff7f 8258:org.codeaurora.gallery/u0a13}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=org.codeaurora.gallery/com.android.gallery3d.app.GalleryActivity bnds=[360,168][536,356] }
          frontOfTask=true task=TaskRecord{495a9e0 #95 A=org.codeaurora.gallery U=0 StackId=1 sz=1}
          taskAffinity=org.codeaurora.gallery
          realActivity=org.codeaurora.gallery/com.android.gallery3d.app.GalleryActivity
          baseDir=/system/priv-app/SnapdragonGallery/SnapdragonGallery.apk
          dataDir=/data/user/0/org.codeaurora.gallery
          stateNotNeeded=false componentSpecified=true mActivityType=0
          compat={320dpi} labelRes=0x7f0b00f1 icon=0x7f030001 theme=0x7f100035
          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=fff5f5f5
          launchFailed=false launchCount=0 lastLaunchTime=-41s697ms
          haveState=true icicle=Bundle[mParcelledData.dataSize=1556]
          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=-41s87ms
          connections=[ConnectionRecord{d694795 u0 CR org.codeaurora.gallery/com.android.gallery3d.app.BatchService:@c63ab4c}]
          resizeMode=RESIZE_MODE_FORCE_RESIZEABLE

    Running activities (most recent first):
      TaskRecord{56df812 #96 A=com.cyanogenmod.filemanager U=0 StackId=1 sz=1}
        Run #1: ActivityRecord{dac4005 u0 com.cyanogenmod.filemanager/.activities.NavigationActivity t96}
      TaskRecord{495a9e0 #95 A=org.codeaurora.gallery U=0 StackId=1 sz=1}
        Run #0: ActivityRecord{1c174fa u0 org.codeaurora.gallery/com.android.gallery3d.app.GalleryActivity t95}

    mResumedActivity: ActivityRecord{dac4005 u0 com.cyanogenmod.filemanager/.activities.NavigationActivity t96}

  Stack #0://Launcher所屬的ActivityStack
  mFullscreen=true
  mBounds=null
    Task id #88
    mFullscreen=true
    mBounds=null
    mMinWidth=-1
    mMinHeight=-1
    mLastNonFullscreenBounds=null
    * TaskRecord{a9ad0b9 #88 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{9997f10 u0 com.android.launcher3/.Launcher t88}]
      askedCompatMode=false inRecents=true isAvailable=true
      lastThumbnail=null lastThumbnailFile=/data/system_ce/0/recent_images/88_task_thumbnail.png
      stackId=0
      hasBeenVisible=true mResizeMode=RESIZE_MODE_FORCE_RESIZEABLE isResizeable=false firstActiveTime=1601263245268 lastActiveTime=1601263245268 (inactive for 38s)
      * Hist #0: ActivityRecord{9997f10 u0 com.android.launcher3/.Launcher t88}
          packageName=com.android.launcher3 processName=com.android.launcher3
          launchedFromUid=0 launchedFromPackage=null userId=0
          app=ProcessRecord{7996cfe 5669: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{a9ad0b9 #88 A=com.android.launcher3 U=0 StackId=0 sz=1}
          taskAffinity=com.android.launcher3
          realActivity=com.android.launcher3/.Launcher
          baseDir=/system/app/XXXLauncher3/XXXLauncher3.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=-1h27m5s112ms
          haveState=true icicle=Bundle[mParcelledData.dataSize=3940]
          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=-39s442ms
          resizeMode=RESIZE_MODE_FORCE_RESIZEABLE

    Task id #94//Task對應的ID
    mFullscreen=true
    mBounds=null
    mMinWidth=-1
    mMinHeight=-1
    mLastNonFullscreenBounds=null
    * TaskRecord{ca4459f #94 A=com.android.systemui U=0 StackId=0 sz=1}
      userId=0 effectiveUid=u0a14 mCallingUid=u0a14 mUserSetupComplete=true mCallingPackage=com.android.systemui
      affinity=com.android.systemui
      intent={flg=0x10804000 cmp=com.android.systemui/.recents.RecentsActivity bnds=[32,1024][688,1840]}
      realActivity=com.android.systemui/.recents.RecentsActivity
      autoRemoveRecents=false isPersistable=false numFullscreen=1 taskType=2 mTaskToReturnTo=1
      rootWasReset=false mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
      Activities=[ActivityRecord{f344048 u0 com.android.systemui/.recents.RecentsActivity t94}]
      askedCompatMode=false inRecents=true isAvailable=true
      lastThumbnail=null lastThumbnailFile=/data/system_ce/0/recent_images/94_task_thumbnail.png
      stackId=0
      hasBeenVisible=true mResizeMode=RESIZE_MODE_RESIZEABLE isResizeable=true firstActiveTime=1601263239735 lastActiveTime=1601263239735 (inactive for 44s)
      * Hist #0: ActivityRecord{f344048 u0 com.android.systemui/.recents.RecentsActivity t94}
          packageName=com.android.systemui processName=com.android.systemui
          launchedFromUid=10014 launchedFromPackage=com.android.systemui userId=0
          app=ProcessRecord{3142ae0 4683:com.android.systemui/u0a14}
          Intent { flg=0x10804000 cmp=com.android.systemui/.recents.RecentsActivity bnds=[32,1024][688,1840] }
          frontOfTask=true task=TaskRecord{ca4459f #94 A=com.android.systemui U=0 StackId=0 sz=1}
          taskAffinity=com.android.systemui
          realActivity=com.android.systemui/.recents.RecentsActivity
          baseDir=/system/priv-app/SystemUI/SystemUI.apk
          dataDir=/data/user_de/0/com.android.systemui
          stateNotNeeded=true componentSpecified=true mActivityType=2
          compat={320dpi} labelRes=0x7f0f0257 icon=0x7f020159 theme=0x7f1301e3
          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=ff212121
          launchFailed=false launchCount=0 lastLaunchTime=-1m32s445ms
          haveState=true icicle=Bundle[mParcelledData.dataSize=500]
          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=3
          frozenBeforeDestroy=false forceNewConfig=false
          mActivityType=RECENTS_ACTIVITY_TYPE
          waitingVisible=false nowVisible=false lastVisibleTime=-46s419ms
          resizeMode=RESIZE_MODE_RESIZEABLE

    Running activities (most recent first):
      TaskRecord{a9ad0b9 #88 A=com.android.launcher3 U=0 StackId=0 sz=1}
        Run #1: ActivityRecord{9997f10 u0 com.android.launcher3/.Launcher t88}
      TaskRecord{ca4459f #94 A=com.android.systemui U=0 StackId=0 sz=1}
        Run #0: ActivityRecord{f344048 u0 com.android.systemui/.recents.RecentsActivity t94}

    mLastPausedActivity: ActivityRecord{9997f10 u0 com.android.launcher3/.Launcher t88}

  //當前持有焦點的Activity
  mFocusedActivity: ActivityRecord{dac4005 u0 com.cyanogenmod.filemanager/.activities.NavigationActivity t96}
  //持有焦點的ActivityStack
  mFocusedStack=ActivityStack{d2f835e stackId=1, 2 tasks} mLastFocusedStack=ActivityStack{d2f835e stackId=1, 2 tasks}
  mSleepTimeout=false
  mCurTaskIdForUser={0=96}
  mUserStackInFront={}
  mActivityContainers={0=ActivtyContainer{0}A, 1=ActivtyContainer{1}A}
  mLockTaskModeState=NONE mLockTaskPackages (userId:packages)=
    0:[]
 mLockTaskModeTasks[]




二.為目標Activity查找/分配或者創建最優Task堆疊

??接著我們前面博客Android四大組件之Activity啟動流程原始碼實作詳解(一)未完成之使命,繼續分析AS.startActivityLocked方法,在正式分析之前,我要在此提前打一個預防針,這是因為該方法涉及的知識點非常多,并且代碼也很多,所以各位小伙們一定打起精神一鼓作氣的將其拿下來(當然小伙們也可以拋開此章節,直接進入第三大章節分析后面的流程,因為這個并不影響Activity啟動的整體流程分析)!
??startActivityLocked方法主要從代碼邏輯上可以分為兩大部分來闡述:

  • 第一大部分:
    1、初始化Activity啟動狀態
    2、計算launchFlag
    3、計算呼叫者的ActivityStack
    4、檢查是否存在復用的TaskRecord
    5、對于存在復用的TaskRecord則進行相應的ActivityStack、TaskRecord的移動(說實話,我也沒有真的搞懂,希望這塊比較有經驗的小伙們能和我一起學習)
    6、計算當前啟動Activity所屬的TaskRecord
    7、把當前啟動的Activity放到所屬TaskRecord的堆疊頂
  • 第二大部分:
    8、最后呼叫resumeFocusedStackTopActivityLocked創建正在啟動的Activity、Paused當前resumed的Activity,
    9、以及resumed當前啟動的Activity

本大章節將重點分析第一大部分內容,不要問我有多少,我只能說很多很多!

//[ActivityStarter.java]
	/*	這里的sourceRecord是指發起呼叫者
		r待啟動的ActivityRecord,這是是在前面創建的一個ActivityRecord物件
		startFlags表示啟動目標Activity的flag,取值為0,
		doResume表示是否要將Activity推入Resume狀態,從上一個方法傳入進來的引數值為true
	*/
    private int startActivityUnchecked(final ActivityRecord r, 
    									ActivityRecord sourceRecord,
            							IVoiceInteractionSession voiceSession, 
            							IVoiceInteractor voiceInteractor,
            							int startFlags, boolean doResume, 
            							ActivityOptions options, 
            							TaskRecord inTask) {

		//初始化Activity啟動的一些狀態,這里主要是根據啟動模式的相關設定進行了一些變數的處理,比如newtask,document等等
        //初始化Activity啟動狀態,獲取launchmode flag 同時解決一些falg和launchmode的沖突
		setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                voiceInteractor);//詳見章節2.1

		//計算目標Activity的launchMode模式
		computeLaunchingTaskFlags();//詳見章節2.2

		//確定發起端的ActivityStack情況
        computeSourceStack();//詳見章節1.3

        mIntent.setFlags(mLaunchFlags);//把前面決議得到的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定義的,
       */
		mReusedActivity = getReusableIntentActivity();//詳見2.4

        final int preferredLaunchStackId =
                (mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID;//此時mOptions為null

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

            /*
			 *這邊處理啟動時設定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) {
                final ActivityRecord top = mReusedActivity.task.performClearTaskForReuseLocked(
                        mStartActivity, mLaunchFlags);
                if (top != null) {
                    if (top.frontOfTask) {
                        top.task.setIntent(mStartActivity);
                    }
                    ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.task);
					// 沒必要新建實體,回呼onNewIntent并將top移至前臺
                    top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
                            mStartActivity.launchedFromPackage);
                }
            }

			// 計算哪個task和activity要移至前臺,必要時會進行task的清理作業
            mReusedActivity = setTargetStackAndMoveToFrontIfNeeded(mReusedActivity);

            if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
                resumeTargetStackIfNeeded();
                return START_RETURN_INTENT_TO_CALLER;
            }
			//根據復用情況設定task
            setTaskFromIntentActivity(mReusedActivity);
	
			//mAddingToTask為true表示要新建,mReuseTask為空表示task被清除了
            if (!mAddingToTask && mReuseTask == null) {
                resumeTargetStackIfNeeded();
                return START_TASK_TO_FRONT;
            }
        }

        if (mStartActivity.packageName == null) {
            if (mStartActivity.resultTo != null && mStartActivity.resultTo.task.stack != null) {
                mStartActivity.resultTo.task.stack.sendActivityResultLocked(
                        -1, mStartActivity.resultTo, mStartActivity.resultWho,
                        mStartActivity.requestCode, RESULT_CANCELED, null);
            }
            ActivityOptions.abort(mOptions);
            return START_CLASS_NOT_FOUND;
        }


        final ActivityStack topStack = mSupervisor.mFocusedStack;
        final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
        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) {
            ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.task);
            topStack.mLastPausedActivity = null;
            if (mDoResume) {
                mSupervisor.resumeFocusedStackTopActivityLocked();
            }
            ActivityOptions.abort(mOptions);
            if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
                return START_RETURN_INTENT_TO_CALLER;
            }
            top.deliverNewIntentLocked(
                    mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
            mSupervisor.handleNonResizableTaskIfNeeded(
                    top.task, preferredLaunchStackId, topStack.mStackId);

            return START_DELIVERED_TO_TOP;
        }

        boolean newTask = false;
        final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
                ? mSourceRecord.task : null;

        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
                && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
            newTask = true;
			// 重用或者新建task
            setTaskFromReuseOrCreateNewTask(taskToAffiliate);

            if (mSupervisor.isLockTaskModeViolation(mStartActivity.task)) {
                Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
                return START_RETURN_LOCK_TASK_MODE_VIOLATION;
            }
            if (!mMovedOtherTask) {
                updateTaskReturnToType(mStartActivity.task, mLaunchFlags, topStack);
            }
        } else if (mSourceRecord != null) {
            if (mSupervisor.isLockTaskModeViolation(mSourceRecord.task)) {
                Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
                return START_RETURN_LOCK_TASK_MODE_VIOLATION;
            }

			// 不是新建task的,重用原activity的task
            final int result = setTaskFromSourceRecord();
            if (result != START_SUCCESS) {
                return result;
            }
        } else if (mInTask != null) {
            if (mSupervisor.isLockTaskModeViolation(mInTask)) {
                Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
                return START_RETURN_LOCK_TASK_MODE_VIOLATION;
            }

            final int result = setTaskFromInTask();
            if (result != START_SUCCESS) {
                return result;
            }
        } else {
            setTaskToCurrentTopOrCreateNewTask();
        }

        mService.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName,
                mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);

        if (mSourceRecord != null && mSourceRecord.isRecentsActivity()) {
            mStartActivity.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
        }
        if (newTask) {
            EventLog.writeEvent(
                    EventLogTags.AM_CREATE_TASK, mStartActivity.userId, mStartActivity.task.taskId);
        }
        ActivityStack.logStartActivity(
                EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.task);
        mTargetStack.mLastPausedActivity = null;
		
		/*把當前啟動的Activity加入TaskRecord以及系結WindowManagerService*/
		mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);
		...
        return START_SUCCESS;
    }

該部分代碼很長,很長!但是沒有辦法,閱讀原始碼就是這么操蛋,我們只能一步步分解,強行分析了!


2.1 AS.setInitialState設定資料初始化狀態

//[ActivityStarter.java]
    private void setInitialState(ActivityRecord r,//表示要啟動的目標Activity資訊
    							ActivityOptions options, //options是附件資訊,此時為null
    							TaskRecord inTask,
            					boolean doResume, //此處的doResume的值為true
            					int startFlags, //這里傳入的startFlags為0
            					ActivityRecord sourceRecord,//發起端的Activity資訊
            					IVoiceInteractionSession voiceSession, 
            					IVoiceInteractor voiceInteractor) {
        reset();//對所有變數進行重置

        mStartActivity = r;//將要啟動的目標Activiyt資訊賦值給mStartActivity 
        mIntent = r.intent;
        mOptions = options;
        mCallingUid = r.launchedFromUid;
        mSourceRecord = sourceRecord;
        mVoiceSession = voiceSession;
        mVoiceInteractor = voiceInteractor;

        mLaunchBounds = getOverrideBounds(r, options, inTask);
		//獲取Activity的啟動模式,這些值是從<activity>標簽中讀取的,即目標Activity所定義的啟動方式
		 // 除了目標Activity定義的啟動模式外,呼叫者也可以設定Activity的啟動模式
    	// 這些引數都體現在Intent的Flags中
        mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP;
        mLaunchSingleInstance = r.launchMode == LAUNCH_SINGLE_INSTANCE;
        mLaunchSingleTask = r.launchMode == LAUNCH_SINGLE_TASK;
        
        /*
        	這里會根據啟動模式來調整flag到NEW_DOCUMEN  如果intent中的和mainfest中的沖突,那么manfest的啟動模式優先
	        FLAG_ACTIVITY_NEW_DOCUMENT是打開一個檔案的標識
	        其處理流程遵循如下邏輯:
           	1、如果此Activity是由singleInstance或者singleTask的話且flag帶了NEW_DOCUMENT,則需要去掉NEW_DOCUMENT和MULTIPLE_TASK的flag
           	2、如果不屬于第一種情況則讀取ActivityInfo中的documentLaunchMode來對flag賦值
        */
        mLaunchFlags = adjustLaunchFlagsToDocumentMode(
                r, mLaunchSingleInstance, mLaunchSingleTask, mIntent.getFlags());
           
   		// 是否為后臺啟動任務的標志位
        mLaunchTaskBehind = r.mLaunchTaskBehind
                && !mLaunchSingleTask && !mLaunchSingleInstance
                && (mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0;

        /*如果是newTask的啟動模式,那么會將resultTo設定為null,
		*這里做了一個處理,這個活動被啟動到一個新的任務中,而且還需要得到請求結果,
		*那么,這是相當混亂的,因此,立即發送回一個取消,讓新的任務繼續啟動像往常一樣,不依賴于它的發起者
		*也就是newTask的啟動模式,是無法獲取到請求結果的*/		
        sendNewTaskResultRequestIfNeeded();

		 //如果設定了NEW_DOCUMENT標志同時此Activity不是其他Activity啟動的
        //則在加上NEW_TASK的標志
        if ((mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
            mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
        }

        if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
            //對于后臺啟動的新任務,可以多任務運行
            if (mLaunchTaskBehind
                    || r.info.documentLaunchMode == DOCUMENT_LAUNCH_ALWAYS) {
                mLaunchFlags |= FLAG_ACTIVITY_MULTIPLE_TASK;
            }
        }

        mSupervisor.mUserLeaving = (mLaunchFlags & FLAG_ACTIVITY_NO_USER_ACTION) == 0;


        mDoResume = doResume;//此時的doReume為true所以不會走入此分支
        //當本次不需要resume時,則設定為延遲resume的狀態
        if (!doResume || !mSupervisor.okToShowLocked(r)) {
            r.delayedResume = true;
            mDoResume = false;
        }

		//此時mOptions為null不會走入此分支
        if (mOptions != null && mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) {
            r.mTaskOverlay = true;
            final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
            final ActivityRecord top = task != null ? task.getTopActivity() : null;
            if (top != null && !top.visible) {

                mDoResume = false;
                mAvoidMoveToFront = true;
            }
        }

		/**FLAG_ACTIVITY_PREVIOUS_IS_TOP:
		*如果給Intent物件設定了這個標記,這個Intent物件被用于從一個存在的Activity中啟動一個新的Activity,
		*那么新的這個	Activity不能用于接受發送給頂層activity的intent,這個新的activity的前一個activity被作為頂部activity
		*如果設定FLAG_ACTIVITY_PREVIOUS_IS_TOP,當前Activity不會作為堆疊頂來啟動新的Activity而是把當前Activity的前一個作為堆疊頂.簡而言之,堆疊ABC啟動D則堆疊變成ABD,所以sourceRecord設定為null
		*/
        mNotTop = (mLaunchFlags & FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;

        mInTask = inTask;
        if (inTask != null && !inTask.inRecents) {
            Slog.w(TAG, "Starting activity in task not in recents: " + inTask);
            mInTask = null;
        }

        mStartFlags = startFlags;
		//我們傳入的startFlags為0不會走入此分支
        if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
            ActivityRecord checkedCaller = sourceRecord;
            if (checkedCaller == null) {
                checkedCaller = mSupervisor.mFocusedStack.topRunningNonDelayedActivityLocked(
                        mNotTop);
            }
            if (!checkedCaller.realActivity.equals(r.realActivity)) {
                //呼叫者 與將要啟動的Activity不相同時,進入該分支
                mStartFlags &= ~START_FLAG_ONLY_IF_NEEDED;
            }
        }
		//是否有影片
        mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0;
    }
    private void reset() {
        mStartActivity = null;
        mIntent = null;
        mCallingUid = -1;
        mOptions = null;

        mLaunchSingleTop = false;
        mLaunchSingleInstance = false;
        mLaunchSingleTask = false;
        mLaunchTaskBehind = false;
        mLaunchFlags = 0;

        mLaunchBounds = null;

        mNotTop = null;
        mDoResume = false;
        mStartFlags = 0;
        mSourceRecord = null;

        mInTask = null;
        mAddingToTask = false;// 表示是否在傳入的inTask中啟動Actiivty,后面會根據實際情況重新設定該變數
        mReuseTask = null;

        mNewTaskInfo = null;
        mNewTaskIntent = null;
        mSourceStack = null;

        mTargetStack = null;
        mMovedOtherTask = false;
        mMovedToFront = false;
        mNoAnimation = false;
        mKeepCurTransition = false;
        mAvoidMoveToFront = false;

        mVoiceSession = null;
        mVoiceInteractor = null;
    }

??在正式開始掰持掰持上述原始碼前,我們先來搗鼓搗鼓幾個概念,因為原始碼中會有涉及到,當然這部分知識我在博客中Android四大組件之Activity啟動流程原始碼實作詳解概要也有提到過:

  • 啟動模式(launchMode):
//[ActivityInfo.java]
public class ActivityInfo extends ComponentInfo
        implements Parcelable {
    ...
    public static final int LAUNCH_MULTIPLE = 0;
    public static final int LAUNCH_SINGLE_TOP = 1;
    public static final int LAUNCH_SINGLE_TASK = 2;
    public static final int LAUNCH_SINGLE_INSTANCE = 3;
	...
}
  • LAUNCH_MULTIPLE(standard):每次啟動新Activity,都會創建新的Activity,這是最常見標準情形,
  • LAUNCH_SINGLE_TOP(singleTop): 當啟動新Acitity,在堆疊頂存在相同Activity,則不會創建新Activity;其余情況同上,
  • LAUNCH_SINGLE_TASK(singleTask):當啟動新Acitity,在堆疊中存在相同Activity(可以是不在堆疊頂),則不會創建新Activity,而是移除該Activity之上的所有Activity;其余情況同上,
  • LAUNCH_SINGLE_INSTANCE(singleInstance):每個Task堆疊只有一個Activity,其余情況同上,
  • 啟動Activity的flag常用值:
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  • FLAG_ACTIVITY_NEW_TASK:將Activity放入一個新啟動的Task,
  • FLAG_ACTIVITY_CLEAR_TASK:啟動Activity時,將目標Activity關聯的Task清除,再啟動新Task,將該Activity放入該Task,該flags跟FLAG_ACTIVITY_NEW_TASK配合使用,
  • FLAG_ACTIVITY_CLEAR_TOP:啟動非堆疊頂Activity時,先清除該Activity之上的Activity,例如Task已有A、B、C3個Activity,啟動A,則清除B,C,類似于SingleTop,
  • FLAG_ACTIVITY_PREVIOUS_IS_TOP:如果給Intent物件設定了這個標記,這個Intent物件被用于從一個存在的Activity中啟動一個新的Activity,那么新的這個 Activity不能用于接受發送給頂層activity的intent,這個新的activity的前一個activity被作為頂部activity,
  • START_FLAG_ONLY_IF_NEEDED:該flag表示只有在需要的時候才啟動目標Activity,也就是說如果呼叫者和被啟動的是一個,那么就沒有必要去進行重復的步驟了

好了,讓我們來開始分析原始碼setInitialState方法的業務邏輯,主要就是進行一些初始化,如下:

  • 首先呼叫通過reset方法,直接將所需要修改的變數進行了重置
  • 優化處理啟動模式:
    1.如果是newTask,則將resultTo設定為空
    2.對FLAG_ACTIVITY_NEW_DOCUMENT的檔案標識的處理
    3.對FLAG_ACTIVITY_PREVIOUS_IS_TOP啟動標志的處理
    4.記錄影片標識

2.2 AS.computeLaunchingTaskFlags計算目標Activity的Task的flag

??是不是被1.1章節涉及到的概念整懵了,這還沒有完呢,又要開始了!這就是我所說的為啥說本大章節是Activity中最難突破的點,那也啥辦法呢只能一點點突破了!在正式開始該部分的原始碼分析前是時候放出看家法寶了(Android四大組件之Activity啟動流程原始碼實作詳解概要有簡單掰扯過)!在這里插入圖片描述

//[ActivityStarter.java]
	/**
	  根據launchMode和Intent中的FLAG_ACTIVITY_NEW_TASK等flag綜合計算activity的啟動模式,
	  結果保存在mLaunchFlags中,計算的程序不僅要考慮目標activity的launchMode,
	  也要考慮原來activity的launchMode和Intent中所帶著的flag
	*/
    private void computeLaunchingTaskFlags() {
		//當呼叫者不是來自Activity,但是又明確指定指定了目標task運行該Activity的話,這個情況比較少見
		if (mSourceRecord == null && mInTask != null && mInTask.stack != null) {
			//查找任務堆疊mInTask的intent資訊,
            final Intent baseIntent = mInTask.getBaseIntent();
            /*
	          TaskRecord由多個activityRecord組成,是我們平時所說的任務堆疊,
	          里面包含著它所管理的activity串列(其中的關系詳見上述圖示)
	          這里回傳第一個沒有結束的activity
			*/
            final ActivityRecord root = mInTask.getRootActivity();
            if (baseIntent == null) {
                ActivityOptions.abort(mOptions);
                throw new IllegalArgumentException("Launching into task without base intent: "
                        + mInTask);
            }

			
            if (mLaunchSingleInstance || mLaunchSingleTask) {
            	/*
            	  如果啟動模式是LAUNCH_SINGLE_INSTANCE或者LAUNCH_SINGLE_TASK,
            	  那么必須保證堆疊是他們所運行的堆疊,否則就拋出例外
            	*/
                if (!baseIntent.getComponent().equals(mStartActivity.intent.getComponent())) {
                    ActivityOptions.abort(mOptions);
                    throw new IllegalArgumentException("Trying to launch singleInstance/Task "
                            + mStartActivity + " into different task " + mInTask);
                }
                if (root != null) {
                    ActivityOptions.abort(mOptions);
                    throw new IllegalArgumentException("Caller with mInTask " + mInTask
                            + " has root " + root + " but target is singleInstance/Task");
                }
            }

			/*
			  如果根部為空,說明里面還沒有activity,可以把我們要啟動的activity作為它的rootTask啟動,
			  所以會對這個task做初始化操作
			*/
            if (root == null) {
                final int flagsOfInterest = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK
                        | FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS;
                mLaunchFlags = (mLaunchFlags & ~flagsOfInterest)
                        | (baseIntent.getFlags() & flagsOfInterest);
                mIntent.setFlags(mLaunchFlags);
                mInTask.setIntent(mStartActivity);
                //mAddingToTask這個變數表示已經找到某個task來放置Activity,
                //有可能是啟動時指定的task還有可能是啟動的sourceTask,反正就是不用再去遍歷尋找task
                mAddingToTask = true;//標記是否增加到堆疊中
            } else if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
            	//當前堆疊根部不為空,但是啟動模式是FLAG_ACTIVITY_NEW_TASK,那么不需要添加新的activity,
            	//只要直接把當前task帶到前臺顯示即可,這個地方需要重點關注一下
                mAddingToTask = false;
            } else {
            	//不是一個空的task,并且也沒有設定FLAG_ACTIVITY_NEW_TASK啟動引數,所以需要添加一個activity到這個task中,設定 mAddingToTask = true
                mAddingToTask = true;
            }
			
			//說明用戶指定的task是可用的,設定mReuseTask = mInTask
            mReuseTask = mInTask;
        } else {
            mInTask = null;
            /*
              此時sourceRecord不為慷訓者用戶沒有指定mInTask,這種情況就需要設定mInTask為null,因為sourceRecord優先級大于mInTask. 這個條件還對特殊情況做了處理,保證要啟動的activity盡量放到SourceRecord 之上
            */
            if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null
                    && mSourceRecord.isFreeform())  {
                mAddingToTask = true;
            }
        }

        if (mInTask == null) {
            if (mSourceRecord == null) {//未指定Task且沒有sourceRecord,//根據呼叫方和要啟動的activty的啟動模式來進行調整,將acitivty啟動模式調整為為newTask
                //呼叫者并不是Activity context,則強制創建新task
                if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {//如果其源任務堆疊也不存在,無法附加要啟動的activity到sourceRecord的task中,則強制新建Task
                    Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
                            "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
                    mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
                }
            } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
                //發起呼叫者的Activity帶有single instance,這種activity只能自己獨自在一個task上,
            	//所以新啟動的activity也要添加FLAG_ACTIVITY_NEW_TASK引數,在新的task上啟動activity
                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
            } else if (mLaunchSingleInstance || mLaunchSingleTask) {
                //目標Activity帶有single instance或者single task,則創建新task
                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
            }
        }
    }

??這細節分析起來的太操蛋了,太多細枝末節的東西了,看來Activity的TaskRecord的處理真的是一個難點啊!在computeLaunchingTaskFlags方法中根據發起端/目的端的launchMode和以及Intent中的FLAG_ACTIVITY_NEW_TASK等flag綜合計算activity的啟動模式,并且主要分了兩個大類情況來處理:

  • 發起端的ActivityRecord資訊記錄為空,但是明確指定要啟動的Activity所在的任務堆疊

    • 要使用的任務堆疊必須有baseIntent,也就是必須有任務堆疊創建時所使用的intent資訊,否則拋例外
    • 如果啟動模式是 singleInstance 或者 singleTask ,那么要使用的任務堆疊的根ActivityRecorkd必須為空,而且啟動任務堆疊所使用的Component必須是當前Component,否則扔例外
    • 如果任務堆疊的根AcitivityRecord為空,那么指定的mInTask其實就是一個新的任務堆疊,修改啟動模式mLaunchFlags ,并且標記mAddingToTask為true
    • 如果任務堆疊的根AcitivityRecord不為空,并且啟動標識有newTask,那么標記mAddingToTask為false
    • 如果任務堆疊的根AcitivityRecord不為空,并且啟動標識不為newTask,那么標記mAddingToTask為true
    • 將要啟動的任務堆疊賦值給可復用的任務堆疊mReuseTask
  • 剩下的情況就是發起端的ActivityRecord資訊記錄不為慷訓者沒有指定mInTask,這種情況直接將指定的mInTask清空

上述操作猛如虎工資2500!當上述兩種情況處理完成以后,上述方法會進行一次判斷處理,如果指定運行的任務堆疊mInTask為空(包括沒有設定,或者后來清空),那么會分情況對啟動標識進行調整:

  • 如果呼叫方AcitivityRecord資訊為空,這時候將要啟動的目標acitivity既無法附加到呼叫方的任務堆疊中,也沒有指定的執行的任務堆疊mInTask存在,那么此時Android系統只能直接強制將其增加newTaks啟動標識,在新的任務堆疊中啟動
  • 如果呼叫方的AcitivityRecord攜帶有啟動標識位singleInstance,那么說明呼叫方需要獨自在一個任務堆疊上,要啟動的目標acitivity也無法附加到其任務堆疊,那么這時候直接將其增加newTaks啟動標識,在新的任務堆疊中啟動,
  • 如果要啟動目標Activity的啟動模式是singTask,或者singleInstance,那么增加newTaks啟動標識

分析至此我們可以得出一個結論就是computeLaunchingTaskFlags的主要功能就是根據發起端/目的端的launchMode和以及Intent中的攜帶的FLAG_ACTIVITY_NEW_TASK等flag綜合計算activity的啟動模式或者說調整啟動目標Activiyt的啟動模式,


2.3 AS.computeSourceStack計算發起方Activity的堆疊

//[ActivityStarter.java]
	//確定發起端的Stack情況
    private void computeSourceStack() {
        if (mSourceRecord == null) {
            mSourceStack = null;
            return;
        }
        if (!mSourceRecord.finishing) {
			//當呼叫者Activity不為空,且不處于finishing狀態,則其所在堆疊賦于sourceStack
            mSourceStack = mSourceRecord.task.stack;
            return;
        }
         //如果呼叫方已經finish了,那么就無法將其作為我們的源任務堆疊了,這時候,要強行添加FLAG_ACTIVITY_NEW_TASK標志使activity啟動到一個新的task中
        if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0) {
            Slog.w(TAG, "startActivity called from finishing " + mSourceRecord
                    + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
            mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
            //保存task的intent資訊和taskinfo資訊是為了新建task的時候嘗試恢復這個task
            mNewTaskInfo = mSourceRecord.info;
            mNewTaskIntent = mSourceRecord.task.intent;
        }
        mSourceRecord = null;
        mSourceStack = null;
    }

??好家伙嘛!終于來了一個簡單點的邏輯了,分為三部分處理:

  • 如果發起方的Activity的mSourceRecord為null,那么發起方Activity的堆疊就置為null嗎,沒有兒子那來的父母不是
  • 如果發起方的mSourceRecord不為null,且沒有被結束的話,直接是從發起方的ActivityRecord拿到ActivityStack物件
  • 而如果發起方已經結束了,則添加newTask標識來啟動新的任務

2.4 AS.getReusableIntentActivity查找可復用的Activity

//[ActivityStarter.java]
    private ActivityRecord getReusableIntentActivity() {
    	/*
    	標識是否可以放入一個已經存在的堆疊
        該條件成立的前提是:
        1.判斷方法是設定了FLAG_ACTIVITY_NEW_TASK,但是并非MULTIPLE_TASK
        2.或者LAUNCH_SINGLE_INSTANCE或者LAUNCH_SINGLE_TASK模式
    	*/
        boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
                (mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
                || mLaunchSingleInstance || mLaunchSingleTask;
		//還要根據目標Activiyt任務堆疊是否為空來進行判斷
        putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
        ActivityRecord intentActivity = null;
        if (mOptions != null && mOptions.getLaunchTaskId() != -1) {//跳過此處
			...
        } else if (putIntoExistingTask) {//可以復用
            if (mLaunchSingleInstance) {//啟動模式是LAUNCH_SINGLE_INSTANCE,那么因為其是一種全域唯一的,需要進行搜索遍
               intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info, false);
            } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
                intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
                        !mLaunchSingleTask);
            } else {
            	//這里是最常見的形式,mStartActivity是需要啟動的Activity,intentActivity是找到的可用的Task中頂部的Activity
                intentActivity = mSupervisor.findTaskLocked(mStartActivity);
            }
        }
        return intentActivity;
    }

??getReusableIntentActivity方法主要是來查找是否有可以重用的activity,這個只對啟動模式為LAUNCH_SINGLE_INSTANCE和LAUNCH_SINGLE_TASK或者FLAG_ACTIVITY_NEW_ TASK不為0的Activity才有用,對于standard的activity,該方法永遠回傳null,
如果putIntoExistingTask為true表示可以進行復用,那么就根據情況進行不同的遍歷查找:

  • 啟動模式是singleInstance,這種啟動模式屬于全域唯一的,通過ASS的 findActivity 方法來查找,引數為目標ActivityRecord,以及布爾型變數false
  • 如果啟動表示有 FLAG_ACTIVITY_LAUNCH_ADJACENT ,也是通過通過ASS的 findActivity 的 findActivity 方法來搜索,引數為是否為singleTask的boolean判斷值
  • 其他情況則呼叫ASS的findTaskLocked方法來查找來搜索

對于以上的查找就不分析了,這個牽涉的東西太多了,后面打算開辟專門章節來分析,


2.5 存在復用ActivityRecord處理

假如此時我們找到了復用的ActivityRecord,我們看看Android是怎么對其進行相關處理的,

//[ActivityStarter.java]
    private void startActivityUnchecked(ActivityRecord r,//表示要啟動的目標Activity資訊
    							ActivityOptions options, //options是附件資訊,此時為null
    							TaskRecord inTask,
            					boolean doResume, //此處的doResume的值為true
            					int startFlags, //這里傳入的startFlags為0
            					ActivityRecord sourceRecord,//發起端的Activity資訊
            					IVoiceInteractionSession voiceSession, 
            					IVoiceInteractor voiceInteractor) {
		...

		 //如果找到了可重用的activity,需要清理掉原來的資訊,并把當前啟動的activity的資訊拷貝進去
		 //做清理和拷貝作業...
		 /*
	       此時當前的Task串列中存在有復用的Activity
	       可能為相同的Activity或者具有相同的affinity的task
	       如果是第一次啟動某個應用或者從adb am中啟動以及第一次啟動Launcher
	       那么復用的TaskRecord為null
		 */
        if (mReusedActivity != null) {
			...			
			//設定當前啟動Activity的Task為復用的Task
            if (mStartActivity.task == null) {
                mStartActivity.task = mReusedActivity.task;
            }
            //設定可以復用Activity所屬的TaskRecord的初始intent為目標Activity的ActivityRecord
            if (mReusedActivity.task.intent == null) {
                mReusedActivity.task.setIntent(mStartActivity);
            }


			//清除task中復用的activity上面的activity
            if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                    || mLaunchSingleInstance || mLaunchSingleTask) {
	               /*
				 	這邊處理啟動時設定FLAG_ACTIVITY_CLEAR_TOP時,要清除復用activity所屬TaskRecord之上的activity
				 	舉個例子:比如復用Task1中存在有Activity A,B,C,D,此時正在啟動的Activity B,那么C和D也要finish
				 	如果找到的可復用的activity的launchMode為LAUNCH_MULTIPLE,并且目標Activity沒有設定為FLAG_ACTIVITY_SINGLE_TOP那么此時也會將可復用activity清除
				 	對于要啟動的activity的啟動模式為LAUNCH_MULTIPLE的,performClearTaskForReuseLocked回傳值top肯定是空的
				 */
                final ActivityRecord top = mReusedActivity.task.performClearTaskForReuseLocked(
                        mStartActivity, mLaunchFlags);   //[詳見章節2.6]
                if (top != null) {
                    if (top.frontOfTask) {//如果該ActivityRecord是所屬任務堆疊的root activity,那么將目標activity設定為堆疊頂的Activity      		
                        top.task.setIntent(mStartActivity);
                    }
                    ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.task);
					// 沒必要新建實體,回呼onNewIntent并將top移至前臺
                    top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
                            mStartActivity.launchedFromPackage);
                }
            }

			// 將復用ActivityRecord所屬TaskRecord移動到頂端,必要時會進行task的清理作業
            mReusedActivity = setTargetStackAndMoveToFrontIfNeeded(mReusedActivity);  //詳見章節[2.7]

			//對于復用Task情況下START_FLAG_ONLY_IF_NEEDED這個FLAG只是resumed
           //該flag表示只有在需要的時候才啟動目標Activity,也就是說如果呼叫者和被啟動的是一個,那么就沒有必要去進行重復的步驟了
            if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
            	//resume顯示到前臺
                resumeTargetStackIfNeeded();
                return START_RETURN_INTENT_TO_CALLER;
            }
            //根據intent情況設定TaskRecord
            setTaskFromIntentActivity(mReusedActivity);//[詳見章節2.8]

			//如果不需要把當前啟動的Activity增加到Task并且不存在復用Task
            //那么僅僅進行resumed程序
            //mAddingToTask為true表示要新建,mReuseTask為空表示task被清除了
            if (!mAddingToTask && mReuseTask == null) {
				//呼叫顯示到前臺
                resumeTargetStackIfNeeded();
                return START_TASK_TO_FRONT;
            }
        }				
	...
}

??在找到可以復用的ActivityRecord以后,startActivityUnchecked方法的處理邏輯遠遠還沒有結束,依舊會繼續著相關邏輯的處理:

  • 如果目標Activity的所屬TaskRecord為null,則將查找到的可復用的Activity的所屬TaskRecord填充
  • 如果可復用Activity所屬TaskRecord的初始Activity為null,則使用目標Activity填充
  • 當我們啟動目標Activity時候如果設定了FLAG_ACTIVITY_CLEAR_TOP標志位,或者目標Activiyt在AndroidManifest中設定了singleTask/singleInstance等啟動模式說明此時需要清掉TaskRecord的資料,這時候會呼叫 performClearTaskForReuseLocked 方法將目標Activity上面的所有的Activity,并將當前activity置頂并回傳頂部的ActivityRecord資訊,最后呼叫 deliverNewIntent 方法
  • 最后不管是否進行了堆疊頂資料的清除,接下來就要將我們可以復用的Activity所在的TaskRecord移動到其所在的ActivityStack的頂部

2.6 TaskRecord.performClearTaskForReuseLocked

??本來想給上述標題添加一個中文注解,但是嗎有時候真的是只可意會不可言傳啊,上面的英文原文反而更覺貼切!

//[TaskRecord.java]

    ActivityRecord performClearTaskForReuseLocked(
    				ActivityRecord newR,//目標Activity 
    				int launchFlags//啟動目標Activity的flag
    				) {
        mReuseTask = true;
        final ActivityRecord result = performClearTaskLocked(newR, launchFlags);
        mReuseTask = false;
        return result;
    }

	final ArrayList<ActivityRecord> mActivities;//存盤TaskRecord管理的ActivityRecord
	
    final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
        int numActivities = mActivities.size();
        //注意,此處是從TaskRecord的堆疊定開始遍歷
        for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
            ActivityRecord r = mActivities.get(activityNdx);
            if (r.finishing) {
                continue;
            }
            //找到了合適的activity,那么所有位于它上面的目標都需要結束
            //注意此處的equal有被重寫,主要通過判斷報名和類名是否一直
            if (r.realActivity.equals(newR.realActivity)) {
                final ActivityRecord ret = r;

                for (++activityNdx; activityNdx < numActivities; ++activityNdx) {
                    r = mActivities.get(activityNdx);
                    if (r.finishing) {
                        continue;
                    }
                    ActivityOptions opts = r.takeOptionsLocked();
                    if (opts != null) {
                        ret.updateOptionsLocked(opts);
                    }
                    if (stack != null && stack.finishActivityLocked(
                            r, Activity.RESULT_CANCELED, null, "clear-task-stack", false)) {
                        --activityNdx;
                        --numActivities;
                    }
                }

  				//如果要復用的activity是multi模式,并且目標Activity沒有設定FLAG_ACTIVITY_SINGLE_TOP啟動模式那么也會呼叫finish結束掉
                if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
                        && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
                    if (!ret.finishing) {
                        if (stack != null) {
                            stack.finishActivityLocked(
                                    ret, Activity.RESULT_CANCELED, null, "clear-task-top", false);
                        }
                        return null;
                    }
                }

                return ret;
            }
        }

        return null;
    }

??performClearTaskForReuseLocked方法不是特別復雜,主要是遍歷TaskRecord物件實體中的ActivityRecord串列mActivities,然后依據一定的規則清除可復用TaskRecord堆疊中的ActivityRecord,其遍歷清除遵循的邏輯如下:

  • 以倒序方式遍歷mActivities(即從尾部開始遍歷,這個也符合堆疊的存盤結構),查找到被復用的Activity
  • 找到被復用的Activity以后,往尾部一次回圈,呼叫ActivityStack的 finishActivityLocked 方法來結束掉對應的Activity,并減少參考數
  • 經過上述遍歷清楚規則以后,最后還有一個規則就是如果要復用的activity是multi模式,并且目標Activity沒有設定FLAG_ACTIVITY_SINGLE_TOP啟動模式那么也會呼叫finish結束掉,也會結束掉當前這個復用的Activity,并且回傳null,如果這個回傳了null的話,就不會呼叫復用的activity的 deliverNewIntent 方法,相當于會重新啟動,

經過上面的步驟以后,不管是否進行了堆疊頂資料的清除,接下來就要將我們可以復用的Activity所在的TaskRecord移動到其所在的ActivityStack的頂部,


2.7 TaskRecord.setTargetStackAndMoveToFrontIfNeeded

??說實話這個博客中的各種Stack和Task的分析,我自己都要吐了,太殘暴了這些個東西,尼瑪,有啥辦法只能硬啃了,我們接著分析setTargetStackAndMoveToFrontIfNeeded看看它是怎么處理我們將要啟動Activity的Stack以及Task的,又是各種的一頓操作啊!

//[TaskRecord.java]
	//這里的入參引數為可復用ActivityRecord 
    private ActivityRecord setTargetStackAndMoveToFrontIfNeeded(ActivityRecord intentActivity) {
    	//獲取可復用ActivityRecord所屬ActivityStack
        mTargetStack = intentActivity.task.stack;
        mTargetStack.mLastPausedActivity = null;
		
		//獲取當前前臺的ActivityStack,ASS中保存著所有的ActivityStack 
        final ActivityStack focusStack = mSupervisor.getFocusedStack();
		//獲取當前前臺ActivityStack堆疊頂的ActivityRecord
        ActivityRecord curTop = (focusStack == null)
                ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);

		//判斷頂部的堆疊是否符合要求(即判斷現在堆疊頂的堆疊是否為能夠復用的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) {
                    intentActivity.setTaskToAffiliateWith(mSourceRecord.task);
                }
                mMovedOtherTask = true;

				 //willclearTask表明是否同時使用了NEW_TASK 和 CLEAR_TASK的flag
                final boolean willClearTask =
                        (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                            == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
                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) {
                        if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
							//這邊是把復用的Task移動到其它的ActivityStack
                            mSupervisor.moveTaskToStackLocked(intentActivity.task.taskId,
                                    launchStack.mStackId, ON_TOP, FORCE_FOCUS, "launchToSide",
                                    ANIMATE);
                        } else {							
                            mTargetStack.moveTaskToFrontLocked(intentActivity.task, mNoAnimation,
                                    mOptions, mStartActivity.appTimeTracker,
                                    "bringToFrontInsteadOfAdjacentLaunch");
                        }
                        mMovedToFront = true;
                    }
                    mOptions = null;
                }
                updateTaskReturnToType(intentActivity.task, mLaunchFlags, focusStack);
            }
        }
        //這邊表示如果不需要Task移動,移動targetStack到前臺
        if (!mMovedToFront && mDoResume) {
            mTargetStack.moveToFront("intentActivityFound");
        }

        mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.task, INVALID_STACK_ID,
                mTargetStack.mStackId);

        if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
            return mTargetStack.resetTaskIfNeededLocked(intentActivity, mStartActivity);
        }
        return intentActivity;
    }

??感覺分析這塊的代碼,每次都是對我腦細胞和精神的一次摧殘啊!我們來看看這個方法的大致流程:

  • 從ASS中找到當前和用戶互動ActivityStack,然后從ActivityStack中找到正在和用戶互動的ActivityRecord,同時找到其所在的任務堆疊(TaskRecord)(此處這幾者之間牽涉的關系詳見章節一)

  • 當發現當前堆疊頂的TaskRecord和我們要啟動的Activity所使用的TaskRecord不是同一個時,這時候如果設定的標志位不會清空堆疊頂的資訊的話,需要將要目標TaskRecord移動到堆疊頂位置,但是這個移動也需要分情況來進行

    1.首先通過getLaunchStack方法獲取目標ActivityStcak資訊intentTask,
    2.這時候會比較我們要啟動的ActivityStack和當前復用的ActivityRecord所對應的ActivityStack作比較,然后根據不同情況走不同的分支

    • 當要啟動的堆疊(launchStack)與目標(mTargetStack)一致,或者要啟動的堆疊為空,則呼叫 moveTaskToFrontLocked 將對應的TaskRecord移動到堆疊頂位置
    • 當要啟動的堆疊(launchStack)為分屏模式且flag滿足FLAG_ACTIVITY_LAUNCH_ADJACENT則呼叫ASS的moveTaskToStackLocked遷移到lauchStack 中
    • 要啟動的堆疊是其它模式則呼叫moveTaskToFrontLocked 將對應的TaskRecord移動到堆疊頂位置

2.7.1 ActivityStack.moveTaskToFrontLocked將TaskRecord移動到堆疊頂

//[ActivityStack.java]
	//此處的入參TaskRecord為可以復用的TaskRecord
    final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, ActivityOptions options,
            AppTimeTracker timeTracker, String reason) {
        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);

        final int numTasks = mTaskHistory.size();//獲取AS中有多少TaskRecord
        final int index = mTaskHistory.indexOf(tr);//獲取入參的tr在當前的AS中的位置
        if (numTasks == 0 || index < 0)  {//例外情況處理
            if (noAnimation) {
                ActivityOptions.abort(options);
            } else {
                updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
            }
            return;
        }

        if (timeTracker != null) {
            for (int i = tr.mActivities.size() - 1; i >= 0; i--) {
                tr.mActivities.get(i).appTimeTracker = timeTracker;
            }
        }

		
        //將Task移動到ActivityStack的堆疊頂端
        insertTaskAtTop(tr, null);

        //獲取到TaskRecord的堆疊頂activity
        ActivityRecord top = tr.getTopActivity();
        if (!okToShowLocked(top)) {//判斷是否可見
            addRecentActivityLocked(top);
            ActivityOptions.abort(options);
            return;
        }
        
        //獲取到ActivityStack中頂部正在運行的Activity
        //這時候stack的topActivity應該是上一步已經移到堆疊復用的Task
        ActivityRecord r = topRunningActivityLocked();
        
 		/*
	 		更新AMS中的focusedActivity
	        這邊會把當前Activity所屬的Stack移到堆疊頂,
	   		并且會更新ActivityStackSupervisor中的
	        mLastFocusedStack、mFocusedStack這兩個變數
      	*/
        mService.setFocusedActivityLocked(r, reason);

        if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
        if (noAnimation) {
            mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
            if (r != null) {
                mNoAnimActivities.add(r);
            }
            ActivityOptions.abort(options);
        } else {
            updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
        }
        
		//呼叫持有焦點的任務堆疊的頂部Activity的onResume()方法
        mStackSupervisor.resumeFocusedStackTopActivityLocked();
        EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);

        if (VALIDATE_TOKENS) {
            validateAppTokensLocked();
        }
    }

??前面我們簡單的總結了moveTaskToFrontLocked的功能就是將TaskRecord移動到ActivityStack,其執行該流程的主要步驟如下:

  • 將可復用TaskRecord插入到ActivityStack的堆疊頂端,至于是那個ActivityStack這個要根據實際情況來定
  • 獲取ActivityStack中頂部正在運行的Activity,這時候stack的topActivity應該是上一步已經移到堆疊復用的Task
  • 更新AMS中的focusedActivity,這邊會把當前Activity所屬的Stack移到堆疊頂
  • 呼叫持有焦點的任務堆疊的頂部Activity的onResume()方法

讓我們回過頭來繼續重看一下setTargetStackAndMoveToFrontIfNeeded方法,可以看到經過我們的分析可知此時當前啟動Activity可以復用的Task已經移動到了堆疊頂了,

到現在為止,我們已經可以復用的ActivityRecord的TaskRecord移動到互動堆疊頂,這時候還沒有over戰斗依然在進行,我們回到章節2.5看到此時會根據實際的情況對可復用的Activity資訊,進行一些整理作業,


2.8 AS.setTaskFromIntentActivity對復用Task根據intent情況繼續處理

//[ActivityStarter.java]
	//注意此時傳入的引數為可復用ActivityRecord
    private void setTaskFromIntentActivity(ActivityRecord intentActivity) {
		//設定了FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK標志位的復用的Task會finish所有的Activity,并且重新
//更新復用Task資訊的當前啟動的Activit
        if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
            mReuseTask = intentActivity.task;
            mReuseTask.performClearTaskLocked();
            mReuseTask.setIntent(mStartActivity);
            mMovedOtherTask = true;
        } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                || mLaunchSingleInstance || mLaunchSingleTask) {
            //清空堆疊,這里跟之前的2.6章節堆疊頂資料的清除操作相似,但是那里處理的是top不為空,這里處理的是top為空的情況,也就是launchMode == ActivityInfo.LAUNCH_MULTIPLE
            ActivityRecord top = intentActivity.task.performClearTaskLocked(mStartActivity,
                    mLaunchFlags);
            if (top == null) {
                mAddingToTask = true;
                mSourceRecord = intentActivity;
                final TaskRecord task = mSourceRecord.task;
                if (task != null && task.stack == null) {
                    mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
                            null /* bounds */, mLaunchFlags, mOptions);
                    mTargetStack.addTask(task,
                            !mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
                }
            }
        }
        任務堆疊頂部的activity和要啟動的activity是同一個 
        else if (mStartActivity.realActivity.equals(intentActivity.task.realActivity)) {
        	//如果是sigleTop,那么就呼叫deliverNewIntent
            if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop)
                    && intentActivity.realActivity.equals(mStartActivity.realActivity)) {
                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity,
                        intentActivity.task);
                if (intentActivity.frontOfTask) {//如果是堆疊的根activity,那么設定
                    intentActivity.task.setIntent(mStartActivity);
                }
                intentActivity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
                        mStartActivity.launchedFromPackage);
            } else if (!intentActivity.task.isSameIntentFilter(mStartActivity)) {
             	//如果不是singleTop,那么認為是需要啟動一個新的activity
                mAddingToTask = true;
                mSourceRecord = intentActivity;
            }
        } else if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
         // 對FLAG_ACTIVITY_RESET_TASK_IF_NEEDED標志出處理,這個主要用于快捷圖示和或者從通知啟動,這種情況需要替換task最上面的activity,所以需要添加activity到task中
            mAddingToTask = true;
            mSourceRecord = intentActivity;
        } else if (!intentActivity.task.rootWasReset) {
            intentActivity.task.setIntent(mStartActivity);
        }
    }

??再接再厲繼續分析,讓我們看看此方法中會對可復用的Activity所在的ActivityStack和TaskRecord做怎么樣的處理:

  • 如果設定了FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK標志位,那么mReuseTask設定為可以復用的intentActivity的任務堆疊
  • 如果設定了清空了FLAG_ACTIVITY_CLEAR_TOP堆疊或者使用了singleInstance或者singleTask的一種,此時會拿到頂部的Task,然后重新添加到ActivityStack中
  • 如果要啟動的Activity和當前頂部的Activity是同一個,會根據情況呼叫onNewIntent方法

此時對于可復用的的Activity所在的TaskRecord處理已經完全完結了,此時已經將TaskRecord移動到堆疊頂位置了,這其中的各種切換邏輯真的是眼花繚亂啊,讓人搞得暈頭轉向!


2.9 存在復用ActivityRecord處理小結

??存在復用ActivityRecord處理的處理已經分析完畢了,各位小伙們真的理解了嗎!我想沒有,因為我也么有繞出來,其中涉及的各種場景太多了,看來還是得帶入實際情況進行分析,但是這個就脫離我們本篇的范疇了,因為我們本篇重點講解Activity的啟動主流程!我們這里還是簡單的對存在復用ActivityRecord處理小結一下,經過上述繁瑣的處理對于可復用的的Activity所在的TaskRecord已經移動到堆疊頂位置了,其基本的呼叫流程如下:

AS.startActivityUnchecked(...)
	mReusedActivity.task.performClearTaskForReuseLocked(...)//清除task中復用的activity上面的activity
	AS.setTargetStackAndMoveToFrontIfNeeded(...)// 將復用ActivityRecord所屬TaskRecordy和ActivityStack移動到頂端,必要時會進行task的清理作業
	AS.setTaskFromIntentActivity(...)//根據intent的情況對復用的Task進行調整

如果讀者朋友們,只想從整理上了解Activity的啟動流程,那么第二大章節閱讀程序中如果有問題也沒有用關系,因為這個并不牽涉到整體流程的分析,


2.10 繼續AS.startActivityUnchecked未完成之使命處理目標Activity不存在的情況

??當存在復用ActivityRecord時,經過前面章節一系列的處理此時已將將可復用activty替換成現在的目標activty,也就不用新建task了,那么生活還得繼續,讓我們跳出reusedActivity不為空的情況,接著繼續后續的分析,

//[ActivityStarter.java]
	/*	這里的sourceRecord是指發起呼叫者
		r是指本次的將要啟動的Activity
		startFlags為啟動目標Activity設定的flag值
		doResume的值為true
	*/
    private int startActivityUnchecked(final ActivityRecord r, 
    									ActivityRecord sourceRecord,
            							IVoiceInteractionSession voiceSession, 
            							IVoiceInteractor voiceInteractor,
            							int startFlags, boolean doResume, 
            							ActivityOptions options, 
            							TaskRecord inTask) {

		...

		 //如果找到了可重用的activity,需要清理掉原來的資訊,并把當前啟動的activity的資訊拷貝進去
		 //做清理和拷貝作業...
        if (mReusedActivity != null) {
			...
        }

        if (mStartActivity.packageName == null) {//例外處理
            if (mStartActivity.resultTo != null && mStartActivity.resultTo.task.stack != null) {
                mStartActivity.resultTo.task.stack.sendActivityResultLocked(
                        -1, mStartActivity.resultTo, mStartActivity.resultWho,
                        mStartActivity.requestCode, RESULT_CANCELED, null);
            }
            ActivityOptions.abort(mOptions);
            return START_CLASS_NOT_FOUND;
        }

		//獲取當前前臺的ActivityStack 以及ActivityRecord 
        final ActivityStack topStack = mSupervisor.mFocusedStack;
        final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
		//是否需要啟動新的Activity標記,一長串的判斷
        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) {//不需要重新啟動,那么使用復用邏輯,將當前activity顯示到前端即可
            ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.task);
            topStack.mLastPausedActivity = null;
            if (mDoResume) {//此時為true
                mSupervisor.resumeFocusedStackTopActivityLocked();
            }
            ActivityOptions.abort(mOptions);
            if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
                return START_RETURN_INTENT_TO_CALLER;
            }
			//呼叫NewIntent方法
            top.deliverNewIntentLocked(
                    mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
            mSupervisor.handleNonResizableTaskIfNeeded(
                    top.task, preferredLaunchStackId, topStack.mStackId);

            return START_DELIVERED_TO_TOP;
        }
    }

??通過代碼分析我們可知此時判斷當前頂部運行的Activity是否是我們所要啟動的Activity,并且啟動模式是singTop或者singleTask,如果是的話,則不會新建Activity,而是呼叫onResume和newIntent方法,因為這兩種模式下,如果頂層是當前Activity的話,都不會啟動新的Activity,這也就是我們常說的 A->B->C ,此時如果C的模式是singleTop,這時候再啟動C的話,堆疊內仍然是A->B->C,這里很明顯不是此種情況!

革命仍未成功還需繼續努力,讓我們接著對startActivityUnchecked后續原始碼繼續分析

//[ActivityStarter.java]
		//表示是否需要創建新的任務堆疊
        boolean newTask = false;
        final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
                ? mSourceRecord.task : null;

        /*
        	如果要啟動的目標Activity沒有對應的resultTo,
        	并且也沒有添加到對應堆疊中
        	而且設定了FLAG_ACTIVITY_NEW_TASK,
        	mAddingToTask為false說明沒有找到對應的堆疊來啟動我們的Activity,
            所以會通過創建或者復用一個堆疊來存放Activity
        */
        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
                && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
            // 該類情況表示要在一個新的任務中啟動Activity
            newTask = true;
			// 重用或者新建task
            setTaskFromReuseOrCreateNewTask(taskToAffiliate);//詳見章節2.10.1
			...
        }
		/*
			當mSourceRecord不為空,把新的ActivityRecord系結到啟動者的TaskRecord上,
         	一般情況下,mSourceRecord就是呼叫者,如本例中的Launcher;
        	但也有特殊情況,舉個例子,如果啟動模式為singleTask,堆疊中又不存在相同的Activity時,
        	mSourceRecord就是堆疊頂的Activity
		*/
		else if (mSourceRecord != null) {        
			...
			// 不是新建task的,重用原activity的task
			// 該類情況下,宿主堆疊就是呼叫者Activity所在的ActivityStack,
        	// 處理方式也大同小異,把宿主任務挪到堆疊頂,針對launchFlags做一些調整
            final int result = setTaskFromSourceRecord();//詳見章節2.10.2
            if (result != START_SUCCESS) {
                return result;
            }
        } else if (mInTask != null) {//啟動時指定了目標堆疊(mInTask),ActivityRecord系結到mInTask,在指定的任務堆疊中啟動Activity

            if (mSupervisor.isLockTaskModeViolation(mInTask)) {
                Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
                return START_RETURN_LOCK_TASK_MODE_VIOLATION;
            }

            final int result = setTaskFromInTask();//詳見2.10.3
            if (result != START_SUCCESS) {
                return result;
            }
        } else {
			//該類情況表示既不是從一個Activity啟動,也不是在新的任務中啟動,都不是則直接找焦點的ActivityStack上堆疊頂的Task,直接系結,這種情況比較少見
			
			setTaskToCurrentTopOrCreateNewTask();//詳見章節2.10.4
        }

??說實話這種邏輯性的代碼分析起來很操蛋,也很容易讓人陷入其中不能自拔,但是生活還得繼續著,代碼也得分析!上述的原始碼主要根據實際情況來繼續進行要啟動的Activity堆疊資訊的處理,這里的處理邏輯又分為如下幾種情況:

  • 如果要啟動的目標Activity沒有對應的resultTo,并且也沒有指定mInTask,并且也沒有添加到對應堆疊中,而且更是設定了FLAG_ACTIVITY_NEW_TASK,此時說明沒有找到對應的堆疊來啟動我們的Activity,則會呼叫setTaskFromReuseOrCreateNewTask繼續處理
  • 如果發起端的mSourceRecord不為空,把新的ActivityRecord系結到啟動者的TaskRecord上
  • 如果啟動時指定了目標堆疊(mInTask),ActivityRecord系結到mInTask,
  • 如果都不是則直接找焦點的ActivityStack上堆疊頂的Task,直接系結

依然還沒有完,Activity啟動中的生命周期一個也沒有看到!尼瑪,還是對上述每種情況下是如何尋找任務堆疊的方式來簡單來進行一下分析!

2.10.1 AS.setTaskFromReuseOrCreateNewTask

//[ActivityStarter.java]
    private void setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate) {
		//獲取當前持有焦點的ActivityStack
        mTargetStack = computeStackFocus(mStartActivity, true, mLaunchBounds, mLaunchFlags,
                mOptions);
		//如果復用的任務堆疊(TaskRecord)為空,說明沒有可以讓我們用來使用的任務堆疊
        if (mReuseTask == null) {
        	//創建任務堆疊
            final TaskRecord task = mTargetStack.createTaskRecord(
                    mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
                    mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
                    mNewTaskIntent != null ? mNewTaskIntent : mIntent,
                    mVoiceSession, mVoiceInteractor, !mLaunchTaskBehind /* toTop */);
             //設定目標Activity的任務堆疊為新建的task
            mStartActivity.setTask(task, taskToAffiliate);
            if (mLaunchBounds != null) {
                final int stackId = mTargetStack.mStackId;
                if (StackId.resizeStackWithLaunchBounds(stackId)) {
                    mService.resizeStack(
                            stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
                } else {
                    mStartActivity.task.updateOverrideConfiguration(mLaunchBounds);
                }
            }
            if (DEBUG_TASKS) Slog.v(TAG_TASKS,
                    "Starting new activity " +
                            mStartActivity + " in new task " + mStartActivity.task);
        } else {
        	//設定目標Activity的任務堆疊為新建的task
            mStartActivity.setTask(mReuseTask, taskToAffiliate);
        }
    }

??setTaskFromReuseOrCreateNewTask方法的處理邏輯比較簡單,總結歸納起來如下:

  • 獲取系統當前的持有焦點的ActivitytStack物件mTargetStack
  • 接著判斷是否存在可以復用的TaskRecord,如果沒有則通過mTargetStack 方法createTaskRecord創建,然后將目標Activity的任務堆疊設定為其創建的task
  • 如果存在可以復用的TaskRecord,則將目標Activity的任務堆疊設定為其可以復用的任務堆疊

2.10.2 AS.setTaskFromSourceRecord

??進入此分支的前提是mSourceRecord不為null,即發起端為Activity,此時會直接把目標ActivityRecord系結到啟動者的TaskRecord上

//[ActivityStarter.java]
    private int setTaskFromSourceRecord() {

		//獲取啟動著的任務堆疊
        final TaskRecord sourceTask = mSourceRecord.task;

		//我們在前面已經將要啟動的Activity所在的Activity堆疊移動到前臺了,所以sourceStack.topTask就是要啟動的Activity所在的堆疊
        //如果目標Activity不允許在螢屏上顯示或者源任務堆疊和目標任務不在同一個堆疊
        final boolean moveStackAllowed = sourceTask.stack.topTask() != sourceTask;
        if (moveStackAllowed) {
			//獲取當前要啟動activity所屬的ActivityStack堆疊
            mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.task,
                    mOptions);
        }

        if (mTargetStack == null) {//目標ActivityStack為空
            mTargetStack = sourceTask.stack;
        } else if (mTargetStack != sourceTask.stack) {
         	//把啟動方的任務堆疊系結到目標ActivityStack上
            mSupervisor.moveTaskToStackLocked(sourceTask.taskId, mTargetStack.mStackId,
                    ON_TOP, FORCE_FOCUS, "launchToSide", !ANIMATE);
        }
        if (mDoResume) {
            mTargetStack.moveToFront("sourceStackToFront");
        }

		//獲取目標ActivityStack的頂部task
        final TaskRecord topTask = mTargetStack.topTask();

		//目標任務堆疊移動到ActivityStack的頂部,也就是設定為焦點
        if (topTask != sourceTask && !mAvoidMoveToFront) {
            mTargetStack.moveTaskToFrontLocked(sourceTask, mNoAnimation, mOptions,
                    mStartActivity.appTimeTracker, "sourceTaskToFront");
        }

		 //如果目標activity還沒有加入到堆疊中,而且啟動標志設定了CLEAR_TOP,那么我們將Activity添加到已經存在的任務堆疊中,并呼叫clear方法清空對應的activity
        if (!mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0) {
            // In this case, we are adding the activity to an existing task, but the caller has

            ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags);
            mKeepCurTransition = true;
            if (top != null) {
                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.task);
                top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);

                mTargetStack.mLastPausedActivity = null;
                if (mDoResume) {
                    mSupervisor.resumeFocusedStackTopActivityLocked();
                }
                ActivityOptions.abort(mOptions);
                return START_DELIVERED_TO_TOP;
            }
        } else if (!mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
            final ActivityRecord top = sourceTask.findActivityInHistoryLocked(mStartActivity);
            if (top != null) {
                final TaskRecord task = top.task;
                task.moveActivityToFrontLocked(top);
                top.updateOptionsLocked(mOptions);
                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task);
                top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
                mTargetStack.mLastPausedActivity = null;
                if (mDoResume) {
                    mSupervisor.resumeFocusedStackTopActivityLocked();
                }
                return START_DELIVERED_TO_TOP;
            }
        }

         //將Activity添加到相應的Task
        mStartActivity.setTask(sourceTask, null);
        if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
                + " in existing task " + mStartActivity.task + " from source " + mSourceRecord);
        return START_SUCCESS;
    }

我們對該流程簡單的分析一下,感覺這來來回回的繞著圈好難啊!

  • 要啟動得Activity堆疊的頂部TaskRecord和啟動方的TaskRecord不一致,則需要移動到其他的ActivityStack去顯示,這里會通過AS.getLaunchStack從有效的displayId中去查找對應的ActivityStack,
  • 如果目標ActivityStack為空,這種屬于默認的情況,會設定為啟動方的ActivityStack,如果目標ActivityStack存在而且和啟動方的ActivityStack不一致,則把啟動方的任務堆疊(TaskRecord)系結到目標所在的ActivityStack上
  • 把目標ActivityStack移動到最前方,同時也移到ActivityDisplay集合的頂部,
  • 最后一種情況AddingToTask為false,打開FLAG_ACTIVITY_CLEAR_TOP,則清除啟動方的TaskRecord中的頂部,頂部不為空,則呼叫newIntent,再根據需要呼叫resume方法,
  • 最后呼叫mStartActivity.setTask將Activity添加到相應的Task

對于上述的各種流程,小伙們能搞懂是最好了,搞不懂也沒有關系,不影響Activity的整體啟動,

2.10.3 AS.setTaskFromInTask

??進入此分支的前提是mInTask不為null,即指定了目標Task

//[ActivityStarter.java]
	//將目標Activity的任務堆疊設定為mInTask
    private int setTaskFromInTask() {
        if (mLaunchBounds != null) {
            mInTask.updateOverrideConfiguration(mLaunchBounds);
            int stackId = mInTask.getLaunchStackId();
            if (stackId != mInTask.stack.mStackId) {
                final ActivityStack stack = mSupervisor.moveTaskToStackUncheckedLocked(
                        mInTask, stackId, ON_TOP, !FORCE_FOCUS, "inTaskToFront");
                stackId = stack.mStackId;
            }
            if (StackId.resizeStackWithLaunchBounds(stackId)) {
                mService.resizeStack(stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
            }
        }

		//根據實際情況檢查是需要創建新的activity還是重新使用任務堆疊頂部的activity
        mTargetStack = mInTask.stack;
		//將mInTask移動到頂部
        mTargetStack.moveTaskToFrontLocked(
                mInTask, mNoAnimation, mOptions, mStartActivity.appTimeTracker, "inTaskToFront");

		//獲取指定任務堆疊頂Activity
        ActivityRecord top = mInTask.getTopActivity();
        if (top != null && top.realActivity.equals(mStartActivity.realActivity) && top.userId == mStartActivity.userId) {
			//如果設定了singleTop或者啟動模式為singleTop或者singleTask,那么呼叫newIntent方法
			if ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                    || mLaunchSingleTop || mLaunchSingleTask) {
                ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.task);
                if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
                    return START_RETURN_INTENT_TO_CALLER;
                }
                top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
                return START_DELIVERED_TO_TOP;
            }
        }

		//mAddingToTask是標記位,標記著是否需要將Activity添加到TaskRecord中,
		// 在之前進行如果已經將目標Activity放入到任務堆疊的話,這里就不需要再次放入了,
		//只需要把TaskRecord移動ActivityStack頂部即可
        if (!mAddingToTask) {
            // We don't actually want to have this activity added to the task, so just
            // stop here but still tell the caller that we consumed the intent.
            ActivityOptions.abort(mOptions);
            return START_TASK_TO_FRONT;
        }

		//將Activity添加到mInTask任務堆疊中
        mStartActivity.setTask(mInTask, null);
        if (DEBUG_TASKS) Slog.v(TAG_TASKS,
                "Starting new activity " + mStartActivity + " in explicit task " + mStartActivity.task);

        return START_SUCCESS;
    }

好嗎,這里的setTaskFromInTask方法也針對不同的情況分別進行了處理:

  • 如果mInTask的頂部和我們要啟動的Activity相同,而且啟動了singleTop或者singleTask,那么就直接呼叫newIntent方法,
  • 如果acitivity在之前的操作中已經添加到mInTask中了,那么這里只需要將mInTask移動到其所在ActivityStack的頂部即可,
  • 剩下的情況就需要將mInTask移動到ActivityStack的頂部,然后Activity添加到mInTask任務堆疊中,

2.10.4 AS.setTaskToCurrentTopOrCreateNewTask

??尼瑪終于只有最好的一種情況了

	//查找當前焦點的ActivityStack上堆疊頂的Task,或者創建一個新的任務堆疊
    private void setTaskToCurrentTopOrCreateNewTask() {
    	//獲取持有焦點的ActivityStack
        mTargetStack = computeStackFocus(mStartActivity, false, null /* bounds */, mLaunchFlags,
                mOptions);
        if (mDoResume) {
            mTargetStack.moveToFront("addingToTopTask");
        }
		//獲取堆疊頂的ActivityRecord,也就是會被我們覆寫的那個Activity
        final ActivityRecord prev = mTargetStack.topActivity();

		//嘗試從持有焦點的ActivityRecord獲取其頂部的任務堆疊,如果獲取不到的話,則創建一個
        final TaskRecord task = (prev != null) ? prev.task : mTargetStack.createTaskRecord(
                        mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
                        mStartActivity.info, mIntent, null, null, true);
		//將activity添加到任務堆疊中
        mStartActivity.setTask(task, null);
        mWindowManager.moveTaskToTop(mStartActivity.task.taskId);
        if (DEBUG_TASKS) Slog.v(TAG_TASKS,
                "Starting new activity " + mStartActivity + " in new guessed " + mStartActivity.task);
    }

尼瑪此時此刻,actvity及其對應的task位置已經安排妥當,現在可以準備讓這個activity開始作業了,真不容易啊!不容易啊!


2.11 AS.startActivityUnchecked未完成之使命ActiviytStack.startActivityLocked

//[ActivityStarter.java]
	/*	這里的sourceRecord是指發起呼叫者
		r是指本次的將要啟動的Activity,startFlags取值為0,
		doResume的值為true
	*/
    private int startActivityUnchecked(final ActivityRecord r, 
    									ActivityRecord sourceRecord,
            							IVoiceInteractionSession voiceSession, 
            							IVoiceInteractor voiceInteractor,
            							int startFlags, boolean doResume, 
            							ActivityOptions options, 
            							TaskRecord inTask) {
        ...    								
		//把當前啟動的Activity加入TaskRecord以及系結WindowManagerService
         mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);
		...          							
}

??本來是打算將前面第二章節涉及的兩大部分都講述完畢,但是一看現在的這個文字量看來只能將第一大部分分析完成了,我們接著繼續

//[ActivityStack.java]
   final void startActivityLocked(
   				ActivityRecord r, //待啟動的ActivityRecord
   				boolean newTask, //是否需要在新的任務中啟動Activity
   				boolean keepCurTransition,//是否需要立即顯示Activity,有些需要Pending的Activity,該值為false;對于初次對于此處而言,該值為true
            	ActivityOptions options
            	) {
		//rTask即之前創建的TaskRecord,或者為查找到的可復用的TaskRecord
        TaskRecord rTask = r.task;
        final int taskId = rTask.taskId;//獲取taskid

		//表示宿主堆疊ActivityStack中沒有歷史任務,或者強制要求在新的任務中啟動Activity
        if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {
            insertTaskAtTop(rTask, r);//插入新創建或復用的TaskRecord到堆疊頂
            mWindowManager.moveTaskToTop(taskId);//同時在Windowmanager中也移動到堆疊頂
        }
        TaskRecord task = null;
        // 進入該條件,表示需要在一個已有的任務中啟動Activity
        // 先設定一個標志位,表示是否需要啟動Activity,默認當然是要顯示;
        // 但是當目標任務之上有全屏的Activity時,該標志位被置為false
        if (!newTask) {//這是表示存在復用的TaskRecord
            boolean startIt = true;
			//遍歷TaskRecord從這兒也可以得出TaskRecord在ActivityStack是以堆疊的方式管理的
			//因為此時是倒序遍歷的
            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
                task = mTaskHistory.get(taskNdx);
                if (task.getTopActivity() == null) {
                    continue;
                }
                if (task == r.task) {//在Task堆疊中找到了復用的Task
                    if (!startIt) {
                        task.addActivityToTop(r);//把當前啟動的Activity加入到復用的Task堆疊頂
                        r.putInHistory();
                        addConfigOverride(r, task);//系結到windowmanager中,此處很重要,但是不會在此分析
                        if (VALIDATE_TOKENS) {
                            validateAppTokensLocked();
                        }
                        ActivityOptions.abort(options);
                        return;
                    }
                    break;
                } else if (task.numFullscreen > 0) {
                    startIt = false;
                }
            }
        }


        if (task == r.task && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) {
            mStackSupervisor.mUserLeaving = false;
            if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
                    "startActivity() behind front, mUserLeaving=false");
        }

	   // 以上完成的操作就是將待顯示的任務放到了宿主堆疊的堆疊頂位置,
	   // 接下來,需要將待顯示的ActivityRecord放到任務的堆疊頂
        task = r.task;
        //當前啟動的Activity加到所屬Task堆疊頂中
        task.addActivityToTop(r);
        // 將一個新的ActivityRecord添加到任務堆疊頂后,需要重新調整FrontOfTask
        task.setFrontOfTask();
	
        r.putInHistory();// 標記目標ActivityRecord已經放置到目標堆疊中
		   /*
		      如果當前ActivityStack不為 HomeStack,或者該ActivityStack中activity個數不為0
		  */
        if (!isHomeStack() || numActivities() > 0) {
            boolean showStartingIcon = newTask;
            ProcessRecord proc = r.app;
            if (proc == null) {
                proc = mService.mProcessNames.get(r.processName, r.info.applicationInfo.uid);
            }
            //對于剛啟動HOME應用時,表示行程ProcessReocrd還為空
            if (proc == null || proc.thread == null) {
                showStartingIcon = true;
            }

            if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
                mWindowManager.prepareAppTransition(TRANSIT_NONE, keepCurTransition);
                mNoAnimActivities.add(r);
            } else {
                mWindowManager.prepareAppTransition(newTask
                        ? r.mLaunchTaskBehind
                                ? TRANSIT_TASK_OPEN_BEHIND
                                : TRANSIT_TASK_OPEN
                        : TRANSIT_ACTIVITY_OPEN, keepCurTransition);
                mNoAnimActivities.remove(r);
            }
            //當前啟動的Activity系結到WIndowManagerService當中
            addConfigOverride(r, task);
            boolean doShow = true;
            if (newTask) {
                if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
                    resetTaskIfNeededLocked(r, r);
                    doShow = topRunningNonDelayedActivityLocked(null) == r;
                }
            } else if (options != null && options.getAnimationType()
                    == ActivityOptions.ANIM_SCENE_TRANSITION) {
                doShow = false;
            }
            if (r.mLaunchTaskBehind) {
                mWindowManager.setAppVisibility(r.appToken, true);
                ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
            } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
                ActivityRecord prev = r.task.topRunningActivityWithStartingWindowLocked();
                if (prev != null) {
                    if (prev.task != r.task) {
                        prev = null;
                    }
                    // (2) The current activity is already displayed.
                    else if (prev.nowVisible) {
                        prev = null;
                    }
                }
                r.showStartingWindow(prev, showStartingIcon);
            }
        } else {
        	// 進入該分支,表示當前的ActivityStack中還沒有任何Activity,則不需要設定任何Activity啟動相關的影片
        	// 只是將待啟動的Activity系結到視窗上
            addConfigOverride(r, task);
            ActivityOptions.abort(options);
            options = null;
        }
        if (VALIDATE_TOKENS) {
            validateAppTokensLocked();
        }
    }

    void addConfigOverride(ActivityRecord r, TaskRecord task) {
        final Rect bounds = task.updateOverrideConfigurationFromLaunchBounds();
       // 將待啟動的Activity系結到視窗上
        mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                (r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, r.info.configChanges,
                task.voiceSession != null, r.mLaunchTaskBehind, bounds, task.mOverrideConfig,
                task.mResizeMode, r.isAlwaysFocusable(), task.isHomeTask(),
                r.appInfo.targetSdkVersion);
        r.taskConfigOverride = task.mOverrideConfig;
    }

該方法的主要主要執行如下的兩大邏輯:

  • 將ActivityRecord放到目標任務的堆疊頂位置,注意,這里有兩個堆疊頂位置:一個是ActivityRecord在TaskRecord中的位置,另一個是TaskRecord在ActivityStack中的位置,前一個是肯定的,但后一個,還得根據實際的情況決定

  • 將待啟動的ActivityRecord系結到了一個視窗,與此同時,Activity啟動的影片也在此設定,為了省略分之細節,所以與WMS互動的視窗操作邏輯就不放出來了

總之經過以上的重重步驟,此時目標Activity對應的Task也創建了,也把當前啟動的Activity加入到Task正確的位置,ActivityStack也移動到了正確位置,此時本章節就分析到這里了!



小結

??閱讀原始碼是很操蛋的事情,同樣的如果將太多的邏輯放在一篇中我想小伙們也會閱讀起來也會操蛋,失去耐心的的,所以本篇章就先到這里了,分析至此我們對前面的篇章做一個小結,我們前面主要干了如下相關的事情:

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

并且通過前面的博文分析,小伙們心里應該有了如下的知識圖譜:

  • 一個ActivityRecord對應一個Activity,保存了一個Activity的所有資訊;但是一個Activity可能會有多個ActivityRecord,因為Activity可以被多次啟動,這個主要取決于其啟動模式,即二者不是一對一的關系,ActivityRecord如果存在一對一的關系,那么也只能說相對于Applicatiion而言
  • 一個TaskRecord由一個或者多個ActivityRecord組成,這就是我們常說的任務堆疊,具有后進先出的特點,
  • ActivityStack則是用來管理TaskRecord的,包含了多個TaskRecord,
  • singleTask 和 singleTop 模式的都會去任務堆疊中遍歷尋找是否已經啟動了相應實體
  • ActivityStackSupervisor 用來管理 ActivityStack 的,APP與 ActivityStack 之間并無必然的聯系,這個要根據實際情況來看,有可能是一個APP對應一個 ActivityStack ,有可能是一個APP對應多個 ActivityStack ,也有可能是多個APP共用一個 ActivityStack
  • 并且啟動模式如果intent中的和mainfest中的沖突,那么manfest的啟動模式優先

好了本篇博客真的只能到此了,雖然本人耗費了很多的時間來對Activity啟動涉及的Task和ActivityStack等調度相關的知識點來進行分析和講解,但是這其中牽涉的東西太多了,并且由于個人能力有限,自我感覺依然分析的不是很清晰,只能大伙見諒了,如果小伙們喜歡歡迎點贊或者能和我討論關于本篇的知識點!

參考博客:
https://www.lizenghai.com/archives/94931.html#getReusableIntentActivity_Activity

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

標籤:其他

上一篇:繼續進階-你還應該掌握的高級技巧

下一篇:iOS開發:加載本地HTML檔案

標籤雲
其他(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