寫這篇文章的時候剛好是2020年春節,全民抗擊肺炎病毒,今年不能出門拜年,只能在家老實呆着,也沒啥事來作,我就尋思着寫點東西,但願能幫助一點人,以爲不錯的話,能夠給個贊哦,哈哈哈~java
分析源碼以前呢,咱先本身先思考幾個問題,分析完成後,看能不能解決掉~app
啓動模式相關源碼都在ActivityStarter.java文件的startActivityUnchecked這個方法, 咱們拆解這個函數來分析一下:( Android 9.0 代碼爲例 )ide
注意下兩個很重要的屬性 mLaunchFlags,mLaunchMode,後面的源碼分析也主要圍繞着兩個屬性在討論函數
mLaunchFlags 包含啓動的flag,好比FLAG_ACTIVITY_NEW_TASK,FLAG_ACTIVITY_CLEAR_TASK等,做用是規定了如何去啓動一個Activity。源碼分析
mLaunchMode 表示啓動模式,好比LAUNCH_SINGLE_INSTANCE,LAUNCH_SINGLE_TASK等。post
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, ActivityRecord[] outActivity) {
// 初始化 mLaunchFlags mLaunchMode
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor);
// 一.計算 mLaunchFlags
computeLaunchingTaskFlags();
//賦值 mSourceTask
computeSourceStack();
mIntent.setFlags(mLaunchFlags);
... ... ...
複製代碼
看 computeLaunchingTaskFlags 方法this
private void computeLaunchingTaskFlags() {
... ... ...
// mSourceRecod 指的是 啓動者,(注意區別於 被啓動者 mStartActivity) mSourceRecord爲null,表示咱們不是從一個Activity來啓動的
// 多是從 一個Service 或者 ApplicationContext 來的
if (mSourceRecord == null) {
if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {
//mInTask mSourceRecord 都爲null, 表示 不是從一個Activity 去啓動另一個Activity,因此無論什麼
//都加上 FLAG_ACTIVITY_NEW_TASK
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
} else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
// 若是 啓動者 本身是 SINGLE_INSTANCE , 那麼無論被啓動的Activity是什麼模式,mLaunchFlags 都加上 FLAG_ACTIVITY_NEW_TASK,
// 這個新 Activity 須要運行在 本身的 棧內
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
} else if (isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
//若是launchMode是 SINGLE_INSTANCE 或者 SINGLE_TASK; mLaunchFlags 添加 FLAG_ACTIVITY_NEW_TASK
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
}
複製代碼
注意圖中有3個地方爲mLaunchFlags添加了FLAG_ACTIVITY_NEW_TASK spa
這一部分仍是startActivityUnchecked方法的一個片斷,緊接着第一部分的源碼3d
//仍是在 startActivityUnchecked 裏面
... ... ...
// 1. 爲SINGLE_INSTANCE查找能夠複用的Activity
// 2. 爲 只有FLAG_ACTIVITY_NEW_TASK而且沒有MULTI_TASK的
// 3. SINGLE_TASK 查找能夠添加的棧
ActivityRecord reusedActivity = getReusableIntentActivity();
... ... ...
複製代碼
咱們順便也跳出startActivityUnchecked方法,去看一看getResuableIntentActivity方法。code
private ActivityRecord getReusableIntentActivity() {
//putIntoExistingTask爲true的條件
//(1)當啓動模式爲SingleInstance;
//(2)當啓動模式爲SingleTask;
//(3)使用了Intent.FLAG_ACTIVITY_NEW_TASK標籤,而且沒有使用FLAG_ACTIVITY_MULTIPLE_TASK標籤
boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
(mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK);
putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
ActivityRecord intentActivity = null;
if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
... ... ...
} else if (putIntoExistingTask) { //putIntoExistingTask爲true時的策略
if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
// SINGLE_INSTANCE 模式下去尋找,這裏目的是findActivityRecord
intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
mStartActivity.isActivityTypeHome());
} else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
... ...
} else {
//這裏要區別於singleInstance調用的方法!!!這裏目的是findTaskRecord
// Otherwise find the best task to put the activity in.
intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId);
}
}
return intentActivity;
}
複製代碼
下圖表示的是三種狀況下putIntoExistingTask爲true
mLaunchMode爲SingleInstance時,走mSupervisor.findActivityLocked;
其餘狀況下,好比咱們的mStartActivity是一個standard模式的Activity,且只加上了FLAG_ACTIVITY_NEW_TASK的flag,會走mSupervisor.findTaskLocked
這裏咱們能夠隱約猜出來出來,被啓動者爲SinglgeInstance的狀況下,咱們是尋找相等的ActivityRecord;而其餘狀況下,咱們找的是一個最合適的棧(從註釋也能夠看出來)。實際上咱們猜的很對,對於SingleInstance的狀況,源碼上也是遍歷查找相同的ActivityRecord。可是對於其餘狀況呢?咱們先思考一個問題,什麼叫最合適的棧?它須要知足什麼樣的條件?
先說一個預備的知識點。咱們的AMS如何管理咱們衆多的Activity的?
從mSupervisor.findTaskLocked進入,咱們最後追蹤到ActivityStack.java
void findTaskLocked(ActivityRecord target, FindTaskResult result) {
... ... ...
// 注意看這裏是倒序遍歷 mTaskHistory
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
... ... ...
} else if (!isDocument && !taskIsDocument
&& result.r == null && task.rootAffinity != null) {
//檢查 是否是 相同的 taskAffinity
if (task.rootAffinity.equals(target.taskAffinity)) {
//當咱們找到taskAffinity符合的棧以後,並無立馬break,而是繼續去尋找,說明task的index越小,表示更適合
result.r = r;
result.matchedByRootAffinity = true;
}
} else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
}
}
複製代碼
最合適的棧 知足兩個條件
1.Activity的taskAffinity和咱們的task的rootAffinity相等
2.不一樣的task的rootAffinity多是相等的,倒序遍歷找到index最小的,也是最合適的
咱們接着分析startActivityUnchecked代碼
// 三. 利用能夠 複用的Activity 或者 複用棧
if (reusedActivity != null) {
... ... ...
// 若是是 SINGLE_INSTANCE 或者 SINGLE_TASK 或者 含有 FLAG_ACTIVITY_CLEAR_TOP 標識
//咱們能夠判斷出來 SINGLE_INSTANCE 或者 SINGLE_TASK 含有 FLAG_ACTIVITY_CLEAR_TOP 的效果
if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isDocumentLaunchesIntoExisting(mLaunchFlags)
|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
//拿 reuseActivity 的棧
final TaskRecord task = reusedActivity.getTask();
// 好比 singleTask 移除要啓動的Activity以前的全部Activity
final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
mLaunchFlags);
if (reusedActivity.getTask() == null) {
reusedActivity.setTask(task);
}
if (top != null) {
if (top.frontOfTask) {
// Activity aliases may mean we use different intents for the top activity,
// so make sure the task now has the identity of the new intent.
top.getTask().setIntent(mStartActivity);
}
//這裏是 SingleInstance 或者 SingleTask ,會執行onNewIntent
deliverNewIntent(top);
}
}
... ... ...
//啓動者和被啓動者是同一個
if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
// We don't need to start a new activity, and the client said not to do anything
// if that is the case, so this is it! And for paranoia, make sure we have
// correctly resumed the top activity.
resumeTargetStackIfNeeded();
return START_RETURN_INTENT_TO_CALLER;
}
if (reusedActivity != null) {
//這裏會去判斷幾種狀況 singleTask singleInstance 和 singleTop
setTaskFromIntentActivity(reusedActivity);
if (!mAddingToTask && mReuseTask == null) {
//singleInstance singleTask 都會走這裏
//1.好比要啓動的Activity是singleTask,且恰好在reusedActivity的棧內
//2.或者一個singleInstance模式的Activity再次被啓動
resumeTargetStackIfNeeded();
if (outActivity != null && outActivity.length > 0) {
outActivity[0] = reusedActivity;
}
return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;
}
}
}
複製代碼
繼續來看一下setTaskFromIntentActivity這個方法
private void setTaskFromIntentActivity(ActivityRecord intentActivity) {
if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
//若是是 FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TASK
final TaskRecord task = intentActivity.getTask();
//清空task
task.performClearTaskLocked();
mReuseTask = task;
mReuseTask.setIntent(mStartActivity);
} else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
//若是是 singleInstance 或者 singleTask 走這裏,清空棧內要啓動的Activity以前的全部Activity們。
ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity,
mLaunchFlags);
//若是top == null 繼續走,不爲null,就結束了這個方法
if (top == null) {
... ... ...
}
} else if (mStartActivity.realActivity.equals(intentActivity.getTask().realActivity)) {
// 判斷是不是 singleTop 模式
// 這種狀況如何復現? singleTop + FLAG_ACTIVITY_NEW_TASK + taskAffinity。
// FLAG_ACTIVITY_NEW_TASK + taskAffinity去指定一個特定存在的棧,且棧頂是咱們要啓動的singleTop模式的activity
if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
|| LAUNCH_SINGLE_TOP == mLaunchMode)
&& intentActivity.realActivity.equals(mStartActivity.realActivity)) {
if (intentActivity.frontOfTask) {
intentActivity.getTask().setIntent(mStartActivity);
}
deliverNewIntent(intentActivity);
} else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) {
... ... ...
}
} else if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
... ... ...
} else if (!intentActivity.getTask().rootWasReset) {
... ... ...
}
}
複製代碼
前提是reusedActivity不爲null,看兩種典型狀況:
1.若是是SingleTask或者是SingleInstance模式的Activity,則執行performClearTaskLocked方法,把要啓動的Activity以前的全部Activity都清除掉。
2.reusedActivity的啓動模式剛好是SingleTop,且也是咱們要啓動的Activity,執行 deliverNewIntent。
上述的兩種狀況可以知足下面的if判斷 !mAddingToTask && mReuseTask == null ,而後return結束。
思考一個問題,狀況1和2作例子來浮現一下?
繼續看 startActivityUnchecked 後面的代碼,這一部分是針對SingleTop模式的處理。
注意哦,以前咱們也遇到了SingleTop模式的處理,就在上面的setTaskFromIntentActivity方法裏。這兩個有什麼區別呢?
區別在這裏的SingleTop模式判斷的棧是咱們當前展現的棧。而setTaskFromIntentActivity裏的判斷前提條件是在咱們的reusedActivity不爲空的狀況下,對reusedActivity進行的判斷,reusedActivity可能並非當前的棧。
... ... ...
//下面這段英文解釋的很好(singleTop模式),當咱們要啓動的Actiivty剛好是當前棧頂的Activity,檢查是否只須要被啓動一次
// If the activity being launched is the same as the one currently at the top, then
// we need to check if it should only be launched once.
final ActivityStack topStack = mSupervisor.mFocusedStack;
final ActivityRecord topFocused = topStack.getTopActivity();
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
|| isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK));
// SINGLE_TOP SINGLE_TASK,要啓動的Activiy剛好在棧頂
// dontStart爲true,表示不會去啓動新的Activity,複用棧頂的Activity
if (dontStart) {
// For paranoia, make sure we have correctly resumed the top activity.
topStack.mLastPausedActivity = null;
if (mDoResume) {
// resume Activity
mSupervisor.resumeFocusedStackTopActivityLocked();
}
ActivityOptions.abort(mOptions);
if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
// We don't need to start a new activity, and the client said not to do
// anything if that is the case, so this is it!
return START_RETURN_INTENT_TO_CALLER;
}
deliverNewIntent(top);
// Don't use mStartActivity.task to show the toast. We're not starting a new activity
// but reusing 'top'. Fields in mStartActivity may not be fully initialized.
mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredWindowingMode,
preferredLaunchDisplayId, topStack);
return START_DELIVERED_TO_TOP;
}
複製代碼
這部分的代碼主要處理SingleTop模式的Activity,要啓動的Activity是SingleTop模式,且也剛好在當前棧的頂部,執行deliverNewIntent。
思考一個小問題,啓動一個SingleTop模式的Activity,而後再次啓動一次它,它的生命週期如何變化呢?
答案是 onCreate -> onStart -> onResume -> onPause -> onNewIntent -> onResume。
繼續分析startActivityUnChecked方法的最後一部分,這部分主要是關於棧是否須要新建。
... ... ...
//必要條件是有FLAG_ACTIVITY_NEW_TASK
if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
//要創建新棧或者使用已經存在的棧,FLAG_ACTIVITY_NEW_TASK是必要條件
newTask = true;
result = setTaskFromReuseOrCreateNewTask(taskToAffiliate, topStack);
} else if (mSourceRecord != null) {
//把被啓動的mStartActivity放在啓動者mSourceRecord所在的棧上
result = setTaskFromSourceRecord();
}
... ... ...
複製代碼
setTaskFromReuseOrCreateNewTask 方法
private int setTaskFromReuseOrCreateNewTask( TaskRecord taskToAffiliate, ActivityStack topStack) {
mTargetStack = computeStackFocus(mStartActivity, true, mLaunchFlags, mOptions);
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 */, mStartActivity, mSourceRecord,
mOptions);
addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds);
} else {
//用舊棧
addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask");
}
... ... ...
複製代碼
咱們能夠看出,若是須要咱們去新建一個棧或者把咱們要啓動的Activity放在已經存在某個棧中,FLAG_ACTIVITY_NEW_TASK是必要條件。
對於第二部分的描述我用一個圖來總結一下。
咱們先總結一下幾個重要的結論:
FLAG_ACTIVITY_NEW_TASK是新建棧或者複用棧的必要條件。SingleTask,SingleInstance會爲mLaunchFlags自動添加FLAG_ACTIVITY_NEW_TASK。也就是說他們都有存在不使用當前棧的可能。SingleInstance是很好理解的,SingleTask須要注意下,關於SingleTask我會詳細說明,不用擔憂。
新建棧或者複用已經存在棧的充要條件是什麼?
(1) FLAG_ACTIVITY_NEW_TASK + taskAffinity(taskAffinity必須與當前顯示的棧的rootAffinity不相同,taskAffinity默認是包名)
(2) FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_MULTIPLE_TASK 這是一定會新建一個棧的
SingleTask能夠理解成FLAG_ACTIVITY_TASK + CLEAR_TOP + (taskAffinity == 該應用包名)
SingleInstance比較特殊,自己FLAG_ACTIVITY_NEW_TASK,特殊的地方其一在於若是啓動過了,會去遍歷找相等的Activity,查找過程不同。而不像SingleTask是去找合適存放的棧,根據taskAffinity來查找。其二在於SingleInstance一個棧只能存放一個Activity,能作到這個的緣由是咱們在根據taskAffinity找到合適的棧的時候,若是發現是SingleInstance模式Activity的棧,直接忽略。
到這裏也算是完結了,咱們回過頭來思考一下最開始提出的幾個問題。
若兩個應用A和B,A中有兩個Activity,A1和A2,B中有一個Activity B1,這三個Activity都是SingleTask模式,那麼啓動順序是A1 -> B1 -> A2,返回的其實是A1。由於SingleTask模式自己含有FLAG_ACTIVITY_NEW_TASK,這裏因爲taskAffinity也和當前展現的棧不相同,因此會去找"合適的"棧放入。
接着前面問題,若是A2是Standard模式 + FlAG_ACTIVITY_NEW_TASK呢? 會回到A1,由於A2和A1在一個棧,相似第一種狀況。
FLAG_ACTIVITY_NEW_TASK 有什麼用?taskAffinity又是啥?這個問題能夠看上面的小結。