Activity啓動模式源碼解析
啓動模式常見應用場景
一般情況下啓動activity的時候不需要指定launchMode,不指定launchMode時,使用的時默認值,默認值時standard。standard屬性在運行時會被解析成FLAG_ACTIVITY_MULTIPLE_TASK,因而源碼中就是用這個flag來處理standard類型的。
如果需要指定launchMode,則有兩種方式,第一種方式時使用xml屬性launchMode來指定,第二種方式是通過在代碼中啓動activity時指定flag。示例如下:
在AndroidManifest中除了通過launchMode標籤設置Activity啓動模式外,還可以通過taskAffinity標籤來標識Activity所屬於的任務,用taskAffinity來給activity分組。不論使用了默認的launchMode還是指定了launchMode,都可以通過查看當前activity返回棧信息來調試。使用如下命令可以查看當前activity的返回棧信息:
adb shell dumpsys activity activities
如果啓動activity 的順序是Main -> A ->B ->C-> A (FLAG_NEW_TASK),未說明的都使用默認的launchMode,則該命令的執行結果如下:
ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
Display #0 (activities from top to bottom):
Stack #1:
mResumedActivity=ActivityRecord{dceffaf u0 com.anly.samples/.activity.AActivity t728}
Task id #728
* TaskRecord{d5cf06d #728 A=com.anly.aactivity U=0 StackId=1 sz=1}
affinity=com.anly.aactivity
intent={flg=0x10000000 cmp=com.anly.samples/.activity.AActivity}
realActivity=com.anly.samples/.activity.AActivity
* Hist #0: ActivityRecord{dceffaf u0 com.anly.samples/.activity.AActivity t728}
packageName=com.anly.samples processName=com.anly.samples
frontOfTask=true task=TaskRecord{d5cf06d #728 A=com.anly.aactivity U=0 StackId=1 sz=1}
taskAffinity=com.anly.aactivity
realActivity=com.anly.samples/.activity.AActivity
resizeMode=RESIZE_MODE_RESIZEABLE
realComponentName=com.anly.samples/.activity.AActivity
Task id #727
* TaskRecord{321c4a2 #727 A=com.anly.samples U=0 StackId=1 sz=5}
affinity=com.anly.samples
intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.anly.samples/.MainActivity}
realActivity=com.anly.samples/.MainActivity
* Hist #4: ActivityRecord{c127d42 u0 com.anly.samples/.activity.CActivity t727}
taskAffinity=com.anly.samples
realActivity=com.anly.samples/.activity.CActivity
realComponentName=com.anly.samples/.activity.CActivity
* Hist #3: ActivityRecord{b05e751 u0 com.anly.samples/.activity.BActivity t727}
taskAffinity=com.anly.samples
* Hist #2: ActivityRecord{ae468bf u0 com.anly.samples/.activity.AActivity t727}
taskAffinity=com.anly.aactivity
* Hist #1: ActivityRecord{dec47c7 u0 com.anly.samples/.MainActivity t727}
taskAffinity=com.anly.samples
realActivity=com.anly.samples/.MainActivity
state=STOPPED stopped=true delayedResume=false finishing=false
mActivityType=APPLICATION_ACTIVITY_TYPE
resizeMode=RESIZE_MODE_RESIZEABLE
realComponentName=com.anly.samples/.MainActivity
從以上返回棧信息中可以看出,整個app的返回棧對應於一個stack#1,
Display #0 (activities from top to bottom):
Stack #1:
並且activity返回棧信息按照從上到下的方式列舉如下,有兩個task,分別爲Task id #728和Task id #727,每個task都包含一些activity。task的屬性是通過TaskRecord來描述的,有幾個重要的屬性值分別是affinity,intent和realActivity。
- 其中affinity就對應於上面說的在xml中指定的affinity,它可以來指定特定的task id,比如這裏是#728。
- intent指定task底部的第一個activity的,其comp屬性值同realActivity
- realActivity指定task底部的第一個activity,該activity的taskAffinity就是該task的affinity,從這裏也可以看出來task和activity之間的各自有一些屬性相互指定
Task id #728
...
Task id #727
...
值得注意的是,如果在xml中指定了taskAffinity,比如指定taskAffinity爲com.anly.aactivity,則在該示例中Task id#728中對應於AActivity,AActivity的屬性是通過ActivityRecord來描述的,它的taskAffinity就是com.anly.aactivity。同樣地,AActivity所在的task的affinity也是com.anly.aactivity。這裏是一一對應的。
* Hist #0: ActivityRecord{dceffaf u0 com.anly.samples/.activity.AActivity t728}
packageName=com.anly.samples processName=com.anly.samples
frontOfTask=true task=TaskRecord{d5cf06d #728 A=com.anly.aactivity U=0 StackId=1 sz=1}
taskAffinity=com.anly.aactivity
realActivity=com.anly.samples/.activity.AActivity
resizeMode=RESIZE_MODE_RESIZEABLE
realComponentName=com.anly.samples/.activity.AActivity
如果沒有在xml中指定taskAffinity,一般這是更常見的使用場景,這種情況下,ActivityRecord中也存在taskAffinity,只不過taskAffinity使用的是默認值,爲包名。比如task id#727中的CActivity,它的taskAffinity就是com.anly.samples,和包名相同。
Task id #727
* TaskRecord{321c4a2 #727 A=com.anly.samples U=0 StackId=1 sz=5}
affinity=com.anly.samples
intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.anly.samples/.MainActivity}
realActivity=com.anly.samples/.MainActivity
* Hist #4: ActivityRecord{c127d42 u0 com.anly.samples/.activity.CActivity t727}
taskAffinity=com.anly.samples
realActivity=com.anly.samples/.activity.CActivity
realComponentName=com.anly.samples/.activity.CActivity
對activity返回棧信息有了總體把握之後,再去閱讀源碼,就很容易理解activity不同的啓動模式在源碼中是怎麼工作的了。
startActivityUncheckedLocked源碼解析
這裏以4.3的源碼爲例,在源碼中處理activity啓動模式的代碼在ActivityStack.java類中,處理邏輯是在startActivityUncheckedLocked這個方法中實現的。它屬於activity啓動流程中的一個環節,activity啓動流程涉及到非常多的技術點,感興趣的同學可以找相關文章閱讀。這裏重點分析startActivityUncheckedLocked方法的工作原理。
final int startActivityUncheckedLocked(ActivityRecord r,
ActivityRecord sourceRecord, int startFlags, boolean doResume,
Bundle options) {
final Intent intent = r.intent;
final int callingUid = r.launchedFromUid;
int launchFlags = intent.getFlags();
// We'll invoke onUserLeaving before onPause only if the launching
// activity did not explicitly state that this is an automated launch.
mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
if (DEBUG_USER_LEAVING) Slog.v(TAG,
"startActivity() => mUserLeaving=" + mUserLeaving);
// If the caller has asked not to resume at this point, we make note
// of this in the record so that we can skip it when trying to find
// the top running activity.
if (!doResume) {
r.delayedResume = true;
}
ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
!= 0 ? r : null;
// If the onlyIfNeeded flag is set, then we can do this if the activity
// being launched is the same as the one making the call... or, as
// a special case, if we do not know the caller then we count the
// current top activity as the caller.
if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
ActivityRecord checkedCaller = sourceRecord;
if (checkedCaller == null) {
checkedCaller = topRunningNonDelayedActivityLocked(notTop);
}
if (!checkedCaller.realActivity.equals(r.realActivity)) {
// Caller is not the same as launcher, so always needed.
startFlags &= ~ActivityManager.START_FLAG_ONLY_IF_NEEDED;
}
}
...
這段代碼重點是int launchFlags = intent.getFlags();這一句,獲取指定的flag。其餘的代碼是做一些容錯或者系統檢查,和launchMode的應用場景沒關係,略過即可。接着往下看
if (sourceRecord == null) {
// This activity is not being started from another... in this
// case we -always- start a new task.
if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
Slog.w(TAG, "startActivity called from non-Activity context; " +
"forcing Intent.FLAG_ACTIVITY_NEW_TASK for: "
+ intent);
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
}
} else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
// The original activity who is starting us is running as a single
// instance... this new activity it is starting must go on its
// own task.
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
} else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
// The activity being started is a single instance... it always
// gets launched into its own task.
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
}
這段代碼是爲了給 launchFlags增加FLAG_ACTIVITY_NEW_TASK。sourceRecord即啓動目標activity的源activity。源碼裏activity是通過ActivityRecord類來管理的,所以sourceRecord的類型是ActivityRecord類型。
- 如果sourceRecord爲null,說明啓動目標activity的源activity爲空。這種情況可能是通過通知欄消息啓動,或者通過scheme協議啓動的。由於沒有上一個activity,這個activit就是第一個啓動的activity,因而需要給它分配新的task,所以要增加FLAG_ACTIVITY_NEW_TASK
- 如果sourceRecord有值,並且sourceRecord的啓動模式是LAUNCH_SINGLE_INSTANCE。注意這裏是sourceRecord而不是目標activity,目標activity是r,它也是ActivityRecord類型。既然sourceRecord的啓動模式是LAUNCH_SINGLE_INSTANCE,根據LAUNCH_SINGLE_INSTANCE的屬性,sourceRecord對應的activity會獨佔一個task,因而需要給目標activity分配新的task,所以要增加FLAG_ACTIVITY_NEW_TASK
- 如果目標activity,也就是r的啓動模式是LAUNCH_SINGLE_INSTANCE或者LAUNCH_SINGLE_TASK。則需要給目標activity分配新的task,所以要增加FLAG_ACTIVITY_NEW_TASK
這三種情況都需要給launchFlags增加FLAG_ACTIVITY_NEW_TASK,這樣就把launchFlags準備好了。下面就是根據launchFlags設置的flag值進行對應的處理了。
不過在這之前又是一個容錯處理,它的意思是,如果目標activity是有返回結果的,而此時目標activity還沒有啓動不應該有返回結果,所以需要把返回結果置爲null,並且返回一個RESULT_CANCELED結果。
if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
// For whatever reason this activity is being launched into a new
// task... yet the caller has requested a result back. Well, that
// is pretty messed up, so instead immediately send back a cancel
// and let the new task continue launched as normal without a
// dependency on its originator.
Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
sendActivityResultLocked(-1,
r.resultTo, r.resultWho, r.requestCode,
Activity.RESULT_CANCELED, null);
r.resultTo = null;
}
下面就是根據launchFlags設置的flag值進行對應的處理了。注意launchFlags是目標activity的flag集合,而不是源activity的。
if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
(launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
這裏有三種情況可以進入下面的處理邏輯。
- launchFlag包含FLAG_ACTIVITY_NEW_TASK且不包含FLAG_ACTIVITY_MULTIPLE_TASK,上文說過FLAG_ACTIVITY_MULTIPLE_TASK對應的是standard啓動模式。所以這個條件就是有FLAG_ACTIVITY_NEW_TASK屬性且不是standard。
- 注意這個條件是個複合條件。其實寫成如下三個條件更好理解
- FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_CLEAR_TASK
- FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_CLEAR_TOP
- FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_SINGLE_TOP
- 目標activity的啓動模式是LAUNCH_SINGLE_TASK
- 目標activity的啓動模式是LAUNCH_SINGLE_INSTANCE
這裏需要說明的是,r.launchMode的值只有四個,以常量的形式定義在ActivityInfo裏,
/**
* Constant corresponding to <code>standard</code> in
* the {@link android.R.attr#launchMode} attribute.
*/
public static final int LAUNCH_MULTIPLE = 0;
/**
* Constant corresponding to <code>singleTop</code> in
* the {@link android.R.attr#launchMode} attribute.
*/
public static final int LAUNCH_SINGLE_TOP = 1;
/**
* Constant corresponding to <code>singleTask</code> in
* the {@link android.R.attr#launchMode} attribute.
*/
public static final int LAUNCH_SINGLE_TASK = 2;
/**
* Constant corresponding to <code>singleInstance</code> in
* the {@link android.R.attr#launchMode} attribute.
*/
public static final int LAUNCH_SINGLE_INSTANCE = 3;
與此對應的是Intent的FLAG常量值,他們定義在Intent類中,常用的常量值有
/**
* If set, the activity will not be launched if it is already running
* at the top of the history stack.
*/
public static final int FLAG_ACTIVITY_SINGLE_TOP = 0x20000000;
/**
* If set, this activity will become the start of a new task on this
* history stack. A task (from the activity that started it to the
* next task activity) defines an atomic group of activities that the
* user can move to. Tasks can be moved to the foreground and background;
* all of the activities inside of a particular task always remain in
* the same order.
*/
public static final int FLAG_ACTIVITY_NEW_TASK = 0x10000000;
/**
* <p>This flag is ignored if
* {@link #FLAG_ACTIVITY_NEW_TASK} is not set.
*/
public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 0x08000000;
/**
* If set, and the activity being launched is already running in the
* current task, then instead of launching a new instance of that activity,
* all of the other activities on top of it will be closed and this Intent
* will be delivered to the (now on top) old activity as a new Intent.
*/
public static final int FLAG_ACTIVITY_CLEAR_TOP = 0x04000000;
雖然Intent的FLAG也有四個,但是他們並不是和ActivityInfo中的值一一對應的。只有SingleTop和Standard是兩者共有的。
回到上面說的三種條件,如果滿足任何一種條件,就會進入後續邏輯,執行下面的操作。
// See if there is a task to bring to the front. If this is
// a SINGLE_INSTANCE activity, there can be one and only one
// instance of it in the history, and it is always in its own
// unique task, so we do a special search.
ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
? findTaskLocked(intent, r.info)
: findActivityLocked(intent, r.info);
這個邏輯非常重要,網上很多介紹launchMode的文章之所以講不清楚甚至講解錯誤就是因爲這段代碼看錯了。
這裏拿目標activity的launchMode去對比,如果不是LAUNCH_SINGLE_INSTANCE,則走findTaskLocked方法,如果是LAUNCH_SINGLE_INSTANCE則走findActivityLocked方法。
理解這段代碼有個小技巧,想一下上文講的返回棧信息,一個stack對應多個task,一個task對應多個activity的結構。用表格簡單描述這種數據結構如下
task #728 | activityA0、activityB0、activityC0、... | |
stack | task#727 | activityA1、activityB1、activityC1、... |
task#726 | activityA2、activityB2、activityC2、... |
這裏判斷的時候,如果不是singleInstance模式,就走findTaskLocked,意思是遍歷某個特定task裏的activity,比如遍歷task#728的activityA0、activityB0、activityC0、...
如果是singleInstance模式,則走findActivityLocked,意思是遍歷所有task裏的所有activity,這種情況會遍歷
- task#728的activityA0、activityB0、activityC0
- task#727的activityA1、activityB1、activityC1
- task#726的activityA2、activityB2、activityC2
findTaskLocked源碼如下,
private ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {
ComponentName cls = intent.getComponent();
if (info.targetActivity != null) {
cls = new ComponentName(info.packageName, info.targetActivity);
}
TaskRecord cp = null;
final int userId = UserHandle.getUserId(info.applicationInfo.uid);
final int N = mHistory.size();
for (int i=(N-1); i>=0; i--) {
ActivityRecord r = mHistory.get(i);
if (!r.finishing && r.task != cp && r.userId == userId
&& r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
cp = r.task;
//Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()
// + "/aff=" + r.task.affinity + " to new cls="
// + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity);
if (r.task.affinity != null) {
if (r.task.affinity.equals(info.taskAffinity)) {
//Slog.i(TAG, "Found matching affinity!");
return r;
}
} else if (r.task.intent != null
&& r.task.intent.getComponent().equals(cls)) {
//Slog.i(TAG, "Found matching class!");
//dump();
//Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
return r;
} else if (r.task.affinityIntent != null
&& r.task.affinityIntent.getComponent().equals(cls)) {
//Slog.i(TAG, "Found matching class!");
//dump();
//Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
return r;
}
}
}
return null;
}
它是從N-1開始向前遍歷的,符合棧後進先出的特性。並且在for循環裏最外層的if條件中有 r.task != cp的判斷,這個判斷就可以保證找到特定的task。並且這裏返回的是目標task頂端第一個activity。需要注意的是,雖然在這裏從N-1開始稱之爲從後向前遍歷,但是從用戶角度看,N-1是返回棧最頂部的activity,0是最底部的activity,是被其他activity蓋住的。也就是這裏說的“後”,對應於視覺上最頂部的activity。
findActivityLocked源碼如下
/**
* Returns the first activity (starting from the top of the stack) that
* is the same as the given activity. Returns null if no such activity
* is found.
*/
private ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
ComponentName cls = intent.getComponent();
if (info.targetActivity != null) {
cls = new ComponentName(info.packageName, info.targetActivity);
}
final int N = mHistory.size();
for (int i=(N-1); i>=0; i--) {
ActivityRecord r = (ActivityRecord)mHistory.get(i);
if (!r.finishing) {
if (r.intent.getComponent().equals(cls)) {
//Slog.i(TAG, "Found matching class!");
//dump();
//Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
return r;
}
}
}
return null;
}
它不區分task,是從後向前遍歷的整個mHistory,而mHistory對應的就是activity返回棧信息中的stack。因而這裏也就清楚singleInstance的工作原理了,他是整個app的stack裏唯一的,這也就是爲什麼稱之爲單例的原因。與此對比,singleTask指的是特定task內唯一的,不同task內可以不唯一。這就是兩者的區別。
繼續往下看
if (taskTop != null) {
if (taskTop.task.intent == null) {
// This task was started because of movement of
// the activity based on affinity... now that we
// are actually launching it, we can assign the
// base intent.
taskTop.task.setIntent(intent, r.info);
// 727 A
}
// If the target task is not in the front, then we need
// to bring it to the front... except... well, with
// SINGLE_TASK_LAUNCH it's not entirely clear. We'd like
// to have the same behavior as if a new instance was
// being started, which means not bringing it to the front
// if the caller is not itself in the front.
ActivityRecord curTop = topRunningNonDelayedActivityLocked(notTop);
// AAcitivty=curTop 728 AAcitivty
if (curTop != null && curTop.task != taskTop.task) {
r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
boolean callerAtFront = sourceRecord == null
|| curTop.task == sourceRecord.task;
if (callerAtFront) {
// We really do want to push this one into the
// user's face, right now.
movedHome = true;
moveHomeToFrontFromLaunchLocked(launchFlags);
moveTaskToFrontLocked(taskTop.task, r, options);
options = null;
}
}
// If the caller has requested that the target task be
// reset, then do so.
if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
taskTop = resetTaskIfNeededLocked(taskTop, r);
}
if ((startFlags&ActivityManager.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.
if (doResume) {
resumeTopActivityLocked(null, options);
} else {
ActivityOptions.abort(options);
}
return ActivityManager.START_RETURN_INTENT_TO_CALLER;
}
if ((launchFlags &
(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK))
== (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK)) {
// The caller has requested to completely replace any
// existing task with its new activity. Well that should
// not be too hard...
reuseTask = taskTop.task;
performClearTaskLocked(taskTop.task.taskId);
reuseTask.setIntent(r.intent, r.info);
} else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
// In this situation we want to remove all activities
// from the task up to the one being started. In most
// cases this means we are resetting the task to its
// initial state.
ActivityRecord top = performClearTaskLocked(
taskTop.task.taskId, r, launchFlags);
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.task.setIntent(r.intent, r.info);
}
logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
top.deliverNewIntentLocked(callingUid, r.intent);
} else {
// A special case: we need to
// start the activity because it is not currently
// running, and the caller has asked to clear the
// current task to have this activity at the top.
addingToTask = true;
// Now pretend like this activity is being started
// by the top of its task, so it is put in the
// right place.
sourceRecord = taskTop;
}
} else if (r.realActivity.equals(taskTop.task.realActivity)) {
// In this case the top activity on the task is the
// same as the one being launched, so we take that
// as a request to bring the task to the foreground.
// If the top activity in the task is the root
// activity, deliver this new intent to it if it
// desires.
if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP)
&& taskTop.realActivity.equals(r.realActivity)) {
logStartActivity(EventLogTags.AM_NEW_INTENT, r, taskTop.task);
if (taskTop.frontOfTask) {
taskTop.task.setIntent(r.intent, r.info);
}
taskTop.deliverNewIntentLocked(callingUid, r.intent);
} else if (!r.intent.filterEquals(taskTop.task.intent)) {
// In this case we are launching the root activity
// of the task, but with a different intent. We
// should start a new instance on top.
addingToTask = true;
sourceRecord = taskTop;
}
} else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
// In this case an activity is being launched in to an
// existing task, without resetting that task. This
// is typically the situation of launching an activity
// from a notification or shortcut. We want to place
// the new activity on top of the current task.
addingToTask = true;
sourceRecord = taskTop;
} else if (!taskTop.task.rootWasReset) {
// In this case we are launching in to an existing task
// that has not yet been started from its front door.
// The current task has been brought to the front.
// Ideally, we'd probably like to place this new task
// at the bottom of its stack, but that's a little hard
// to do with the current organization of the code so
// for now we'll just drop it.
taskTop.task.setIntent(r.intent, r.info);
}
// 不是NEW_TASK,也不是CLEAR_TASK
// 且找到了那個目標activity,不論是singleTask,singleInstance還是singleTop
if (!addingToTask && reuseTask == null) {
// We didn't do anything... but it was needed (a.k.a., client
// don't use that intent!) And for paranoia, make
// sure we have correctly resumed the top activity.
if (doResume) {
resumeTopActivityLocked(null, options);
} else {
ActivityOptions.abort(options);
}
return ActivityManager.START_TASK_TO_FRONT;
}
}
如果找到了taskTop,則進入喚醒邏輯,否則創建新的。喚醒邏輯裏有兩個因素會影響到具體執行流程,一個是剛纔找到的要被喚醒的taskTop,一個是當前返回棧棧頂的activity,源碼裏稱之爲curTop。
- 如果taskTop和curTop各自所屬的task不同,則把taskTop所屬的task移動到頂部,讓該task可見。具體代碼是moveTaskToFrontLocked方法。如果相同則不需要移動。執行到這裏就可以保證目標activity所在的task已經處於前臺可見。
- 接下來看目標activity的launchFlags,如果有FLAG_ACTIVITY_CLEAR_TASK,則把同一task內的所有activity都clear掉,具體代碼是performClearTaskLocked(taskTop.task.taskId)。此時目標activity等待後續喚醒,task被標記爲複用。
- 如果目標activity的launchFlags包含有FLAG_ACTIVITY_CLEAR_TOP,或者launchMode是LAUNCH_SIGNLE_TASK||LAUNCH_SIGNLE_INSTANCE,則把同一task內的它上方的所有activity都clear掉,具體代碼是performClearTaskLocked(taskTop.task.taskId, r, launchFlags)。注意區分performClearTaskLocked這兩個方法,這是三個參數的重載方法,上面是一個參數的。此時通過deliverNewIntentLocked調用目標activity的onNewIntent方法。
- 如果目標activity的launchFlags包含有FLAG_ACTIVITY_SINGLE_TOP或者launchMode是LAUNCH_SINGLE_TOP,此時通過deliverNewIntentLocked調用目標activity的onNewIntent方法。
- 最後執行resumeTopActivityLocked,該方法回調用目標activity的onResume方法
這裏就處理完了上文三種情況。爲了方便對比這裏再次羅列下那三種情況。
- launchFlag包含FLAG_ACTIVITY_NEW_TASK且不包含FLAG_ACTIVITY_MULTIPLE_TASK,上文說過FLAG_ACTIVITY_MULTIPLE_TASK對應的是standard啓動模式。所以這個條件就是有FLAG_ACTIVITY_NEW_TASK屬性且不是standard。注意這個條件是個複合條件。其實寫成如下三個條件更好理解。
- FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_CLEAR_TASK
- FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_CLEAR_TOP
- FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_SINGLE_TOP
- 目標activity的啓動模式是LAUNCH_SINGLE_TASK
- 目標activity的啓動模式是LAUNCH_SINGLE_INSTANCE
那這三種情況處理完之後,可以看到除了FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_CLEAR_TASK的情況return之外,其餘情況並沒有return而是繼續向下執行。繼續向下執行的情況有
- FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_SINGLE_TOP
- FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_CLEAR_TASK
- LAUNCH_MULTIPLE
- LAUNCH_SINGLE_TOP
- LAUNCH_SINGLE_TASK
- LAUNCH_SINGLE_INSTANCE
- FLAG_ACTIVITY_MULTIPLE_TASK
- FLAG_ACTIVITY_CLEAR_TASK
- FLAG_ACTIVITY_CLEAR_TOP
- FLAG_ACTIVITY_SINGLE_TOP
這些情況在執行過程中有些情況的組合會被處理且提前return,比如FLAG_ACTIVITY_SINGLE_TOP,LAUNCH_SINGLE_TOP和LAUNCH_SINGLE_TASK。不滿足提前return條件的則繼續向下執行,
if (r.packageName != null) {
// 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.
ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
if (top != null && r.resultTo == null) {
if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
if (top.app != null && top.app.thread != null) {
if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
// For paranoia, make sure we have correctly
// resumed the top activity.
if (doResume) {
resumeTopActivityLocked(null);
}
ActivityOptions.abort(options);
if ((startFlags&ActivityManager.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 ActivityManager.START_RETURN_INTENT_TO_CALLER;
}
top.deliverNewIntentLocked(callingUid, r.intent);
return ActivityManager.START_DELIVERED_TO_TOP;
}
}
}
}
} else {
if (r.resultTo != null) {
sendActivityResultLocked(-1,
r.resultTo, r.resultWho, r.requestCode,
Activity.RESULT_CANCELED, null);
}
ActivityOptions.abort(options);
return ActivityManager.START_CLASS_NOT_FOUND;
}
先拿到當前前臺可見的activity這裏命名爲top,如果top非空,且top.realActivity和目標r.realActivity一致,則繼續判斷是否有FLAG_ACTIVITY_SINGLE_TOP或者FLAG_ACTIVITY_SINGLE_TASK,LAUNCH_SINGLE_TASK。如果滿足條件則通過deliverNewIntentLocked調用目標activity的onNewIntent方法。
else分支是容錯處理,略過,繼續往下看。
// Should this be considered a new task?
if (r.resultTo == null && !addingToTask
&& (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
if (reuseTask == null) {
// todo: should do better management of integers.
mService.mCurTask++;
if (mService.mCurTask <= 0) {
mService.mCurTask = 1;
}
r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in new task " + r.task);
} else {
r.setTask(reuseTask, reuseTask, true);
}
newTask = true;
if (!movedHome) {
moveHomeToFrontFromLaunchLocked(launchFlags);
}
} else if (sourceRecord != null) {
if (!addingToTask &&
(launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
// In this case, we are adding the activity to an existing
// task, but the caller has asked to clear that task if the
// activity is already running.
ActivityRecord top = performClearTaskLocked(
sourceRecord.task.taskId, r, launchFlags);
keepCurTransition = true;
if (top != null) {
logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
top.deliverNewIntentLocked(callingUid, r.intent);
// For paranoia, make sure we have correctly
// resumed the top activity.
if (doResume) {
resumeTopActivityLocked(null);
}
ActivityOptions.abort(options);
return ActivityManager.START_DELIVERED_TO_TOP;
}
} else if (!addingToTask &&
(launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
// In this case, we are launching an activity in our own task
// that may already be running somewhere in the history, and
// we want to shuffle it to the front of the stack if so.
int where = findActivityInHistoryLocked(r, sourceRecord.task.taskId);
if (where >= 0) {
ActivityRecord top = moveActivityToFrontLocked(where);
logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
top.updateOptionsLocked(options);
top.deliverNewIntentLocked(callingUid, r.intent);
if (doResume) {
resumeTopActivityLocked(null);
}
return ActivityManager.START_DELIVERED_TO_TOP;
}
}
// An existing activity is starting this new activity, so we want
// to keep the new one in the same task as the one that is starting
// it.
r.setTask(sourceRecord.task, sourceRecord.thumbHolder, false);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in existing task " + r.task);
} else {
// This not being started from an existing activity, and not part
// of a new task... just put it in the top task, though these days
// this case should never happen.
final int N = mHistory.size();
ActivityRecord prev =
N > 0 ? mHistory.get(N-1) : null;
r.setTask(prev != null
? prev.task
: new TaskRecord(mService.mCurTask, r.info, intent), null, true);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in new guessed " + r.task);
}
一般情況下,都是用默認值或者僅僅指定某一個啓動屬性,這時都會走這塊邏輯。具體是
- 如果指定了目標activity的FALG_ACTIVITY_NEW_TASK,且在上面的邏輯處理中找到了可以複用的目標activity,則進入詞條件。但是這裏並沒有return。而是繼續向下執行,直到調用startActivityLocked結束。
- 若無可以複用的task,則走if(reuseTask == null)的邏輯。mCurTask++且new一個新的TaskRecord。
- 若有,則複用reuseTask。
- 否則,檢測是否指定了FLAG_ACTIVITY_CLEAR_TOP,如果指定了則走clearTop 的邏輯,同上。這裏會return。
- 否則,檢測是否指定了FLAG_ACTIVITY_REORDER_TO_FRONT,一般開發者不會指定這個,略
- 如果均未指定,這種情況下就是默認值(standard)的情況。則直接複用父類的task。
// An existing activity is starting this new activity, so we want // to keep the new one in the same task as the one that is starting // it. r.setTask(sourceRecord.task, sourceRecord.thumbHolder, false);
繼續執行,通過startActivityLocked來啓動一個新的activity。
startActivityLocked(r, newTask, doResume, keepCurTransition, options);
-
否則,就如註釋所說these days this case should never happen.略。
至此,所以流程處理完畢。
總結一下,理解launchMode,需要現在腦海中建立那個activity返回棧信息從而有個總體印象,然後再去閱讀代碼。代碼分成兩大部分,分別對應上文說的三種情況以及後續的情況。這些情況有些在執行時就提前return完成了處理,有些則不會return繼續向下執行直到方法結束。這也是整體代碼比較難懂的原因。只要梳理清楚了那些條件會return哪些不會,正確的執行流程就可以掌握了,然後在仔細分析具體的複用機制就可以完全掌握了。
感興趣的或者有問題的歡迎留言交流。