standard、singleTop、singleTask和singleInstance原理分析
關鍵函式入口:startActivityUncheckedLocked
我們知道啟動一個Activity有四種方式:standard(標準啟動模式)、singleTop、singleTask、singleInstance。先簡述一個四種啟動方式。standard:進入啟動task,每次都建立新的例項進入task頂部;singleTop: 進入啟動task,如果已有例項並且在task頂部不建立新例項,呼叫原例項的onNewIntent(),其它情況都建立新的例項進入task頂部;singleTask:如果某個Activity是singleTask模式,那麼Task棧中將會只有一個該Activity的例項。例如:現在棧的情況為:A B C D。B的Launch mode為singleTask,此時D通過Intent跳轉到B,則棧的情況變成了:A B。而C和D被彈出銷燬了,也就是說位於B之上的例項都被銷燬了。singleInstance:進入新的task,並且此task內只存在此一個activity ,不再加入別的activity。 如果task中存在例項,執行例項的onNewIntent()。 道理我都懂,沒有原始碼還是不安心啊。下面來分析下原始碼(基於Android5.1)。 與本章內容相關的方法是ActivityStackSupervisor#startActivityUncheckedLocked。函式比較長,我們分段來進行介紹。 startActivityUncheckedLocked第一個引數r表示要啟動的Activity,第二個引數sourceRecord表示進行啟動Activity動作的Activity,即動作發起者。doResume引數表示是否立即啟動Activity,options引數與Activity轉場動畫ActivityOptions有關,inTask指定啟動在哪個TaskRecord。通常,doResume引數為true,options引數為null,inTask引數為null,r和sourceRecord引數不為null。
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, Bundle options, TaskRecord inTask) { ...
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
這個根據Activity的launchMode決定launchSingleTop,launchSingleInstance和launchSingleTask的值。這些資訊由PackageParser#parseActivity解析android:launchMode欄位得到。
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
final Intent intent = r.intent; ... final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP; final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE; final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;
addingToTask表示是啟動的Activity是否是新增到一個已經存在的TaskRecord當中去,reuseTask表示複用的TaskRecord。如果發起者sourceRecord表示的Activity的launchMode為singleInstance,則設定FLAG_ACTIVITY_NEW_TASK標誌位,所以在singleInstance模式啟動的Activity中啟動一個新的Activity,是會預設帶上FLAG_ACTIVITY_NEW_TASK啟動標誌位的,這也是我們看到的在singleInstance模式啟動的Activity中啟動的新的Activity跟發起者Activity不在同一個TaskRecord的原因。但是帶上FLAG_ACTIVITY_NEW_TASK啟動標誌位不意味著一定會新起一個TaskRecord,原因待會介紹。如果要啟動的Activity的launchMode如果是singleTask和singleinstance,則帶上FLAG_ACTIVITY_NEW_TASK標誌位。 如果發起者Activity處於finish狀態,不宜將啟動的Activity放在發起者Activity的TaskRecord中,因為那個TaskRecord可能已經是空的(沒有非finish的Activity)了,則啟動標誌位要帶上FLAG_ACTIVITY_NEW_TASK。於是,將發起者Activity的ActivityInfo(儲存了一些解析AndroidManifest得到的Activity的資訊)和發起者Activity所在的Taskrecord的Intent(一般來說是根Activity的Intent)分別儲存在newTaskInfo和newTaskIntent,在建立新的TaskRecord時用到。如果發起者Activity不處於finish狀態,sourceStack設定為發起者Activity所在的ActivityStack。
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
boolean addingToTask = false;
TaskRecord reuseTask = null;
...
if (sourceRecord == null && inTask != null && inTask.stack != null) {
//不走此分支
...
} else {
inTask = null;
}
if (inTask == null) {
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 && inTask == null) {
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.
//如果發起者的Activity的launchMode為singleInstance,則設定FLAG_ACTIVITY_NEW_TASK標誌位
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
} else if (launchSingleInstance || launchSingleTask) {
// The activity being started is a single instance... it always
// gets launched into its own task.
//啟動的Activity的launchMode如果是singleTask和singleinstance,則帶上FLAG_ACTIVITY_NEW_TASK標誌位
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
}
}
ActivityInfo newTaskInfo = null;
Intent newTaskIntent = null;
ActivityStack sourceStack;
if (sourceRecord != null) {
if (sourceRecord.finishing) {
// If the source is finishing, we can't further count it as our source. This
// is because the task it is associated with may now be empty and on its way out,
// so we don't want to blindly throw it in to that task. Instead we will take
// the NEW_TASK flow and try to find a task for it. But save the task information
// so it can be used when creating the new task.
if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
Slog.w(TAG, "startActivity called from finishing " + sourceRecord
+ "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
newTaskInfo = sourceRecord.info;
newTaskIntent = sourceRecord.task.intent;
}
sourceRecord = null;
sourceStack = null;
} else {
sourceStack = sourceRecord.task.stack;
}
} else {
sourceStack = null;
}
boolean movedHome = false;
ActivityStack targetStack;
//更新啟動的Activity的啟動標誌位
intent.setFlags(launchFlags);
;儘管使用了FLAG_ACTIVITY_NEW_TASK啟動標誌位或者singleTask/singleInstance的launchMode,系統還是先檢查是否可以使用現有的TaskRecord,而不是立刻建立一個新的TaskRecord。singleTask模式下,使用findTaskLocked來查詢一個“符合要求”的Activity;singleInstance模式下,使用findActivityLocked來查詢一個“符合要求”的Activity。“符合要求”涉及到taskAffinity等的屬性,稍後再分析。如果找到了一個“符合要求”的Activity即程式碼中的intentActivity,將啟動的Activity的TaskRecord設定為intentActivity的TaskRecord,將目標啟動的ActivityStack設定為intentActivity的ActivityStack。 如果我們發現當前resume的Activity的TaskRecord不是我們找到的“符合要求”的Activity所在的TaskRecord時,需要將“符合要求”的Activity所在的TaskRecord轉換成前臺的TaskRecord。moveTaskToFrontLocked就是將原本處於後臺的TaskRecord拿到前臺去。 現在我們找到了“符合要求”的TaskRecord,也把這個TaskRecord拿到前臺。剩下就是檢查我們要啟動的Activity是否在這個TaskRecord中了。performClearTaskLocked用來清理這個TaskRecord中我們要啟動的Activity上面的Activity。在以下兩種情況performClearTaskLocked會返回null:1.在TaskRecord中找不到要啟動的Activity;2.找到了要啟動的Activity,但是其launchMode是standard且啟動標誌位沒有FLAG_ACTIVITY_SINGLE_TOP且沒有finish。在找到要啟動的Activity的情況下,均要將其上面的Activity通過finishActivityLocked清理掉,如果符合上述2的情況下,還要額外清理找到的Activity。在performClearTaskLocked返回null的情況下,addingToTask設為true,表示新增到一個已存在的TaskRecord中,sourceRecord設為“符合要求”的Activity。在performClearTaskLocked不返回null的情況下,addingToTask為false,先呼叫其的onNewIntent方法,後面會直接呼叫resumeTopActivityLocked啟動一個處於頂部的Activity,也就是尋找到的Activity。如果頂部的Activity不符合上述2的情況,就會造成頂部多個重複例項的情況。所以,對於不符合上述2的情況的Activity,即使它“符合要求”,也要將其去掉,然後重新建立一個例項。 在singleTask或singleInstance模式或帶有FLAG_ACTIVITY_NEW_TASK啟動標誌位的情況下,對於可以已存在於TaskRecord中無需重新建立例項的Activity,直接用resumeTopActivityLocked啟動後返回。如果需要重新建立例項(addingToTask為true),則進入下一步。
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
if (((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
(launchFlags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| launchSingleInstance || launchSingleTask) {
// If bring to front is requested, and no result is requested and we have not
// been given an explicit task to launch in to, and
// we can find a task that was started with this same
// component, then instead of launching bring that one to the front.
if (inTask == null && r.resultTo == null) {
// 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.
//查詢“符合要求”的Activity
ActivityRecord intentActivity = !launchSingleInstance ?
findTaskLocked(r) : findActivityLocked(intent, r.info);
if (intentActivity != null) {
if (isLockTaskModeViolation(intentActivity.task)) {
showLockTaskToast();
Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
if (r.task == null) {
//更新TaskRecod
r.task = intentActivity.task;
}
//目標ActivityStack
targetStack = intentActivity.task.stack;
targetStack.mLastPausedActivity = null;
if (DEBUG_TASKS) Slog.d(TAG, "Bring to front target: " + targetStack
+ " from " + intentActivity);
targetStack.moveToFront("intentActivityFound");
if (intentActivity.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.
intentActivity.task.setIntent(r);
}
// 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.
final ActivityStack lastStack = getLastStack();
ActivityRecord curTop = lastStack == null?
null : lastStack.topRunningNonDelayedActivityLocked(notTop);
boolean movedToFront = false;
if (curTop != null && (curTop.task != intentActivity.task ||
curTop.task != lastStack.topTask())) {
//非前臺TaskRecord,將其變成前臺TaskRecord
r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
if (sourceRecord == null || (sourceStack.topActivity() != null &&
sourceStack.topActivity().task == sourceRecord.task)) {
// We really do want to push this one into the
// user's face, right now.
if (launchTaskBehind && sourceRecord != null) {
intentActivity.setTaskToAffiliateWith(sourceRecord.task);
}
movedHome = true;
targetStack.moveTaskToFrontLocked(intentActivity.task, r, options,
"bringingFoundTaskToFront");
if ((launchFlags &
(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
// Caller wants to appear on home activity.
intentActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
}
options = null;
movedToFront = true;
}
}
// If the caller has requested that the target task be
// reset, then do so.
if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
intentActivity = targetStack.resetTaskIfNeededLocked(intentActivity, 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) {
resumeTopActivitiesLocked(targetStack, null, options);
// Make sure to notify Keyguard as well if we are not running an app
// transition later.
if (!movedToFront) {
notifyActivityDrawnForKeyguard();
}
} 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 = intentActivity.task;
reuseTask.performClearTaskLocked();
reuseTask.setIntent(r);
} else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
|| launchSingleInstance || launchSingleTask) {
// 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 =
intentActivity.task.performClearTaskLocked(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);
}
ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT,
r, top.task);
top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
} 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 = intentActivity;
}
} else if (r.realActivity.equals(intentActivity.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 || launchSingleTop)
&& intentActivity.realActivity.equals(r.realActivity)) {
ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r,
intentActivity.task);
if (intentActivity.frontOfTask) {
intentActivity.task.setIntent(r);
}
intentActivity.deliverNewIntentLocked(callingUid, r.intent,
r.launchedFromPackage);
} else if (!r.intent.filterEquals(intentActivity.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 = intentActivity;
}
} 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 = intentActivity;
} else if (!intentActivity.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.
intentActivity.task.setIntent(r);
}
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.
//所有的TaskRecord都已經調整好,直接啟動最頂層的Activity
if (doResume) {
targetStack.resumeTopActivityLocked(null, options);
if (!movedToFront) {
// Make sure to notify Keyguard as well if we are not running an app
// transition later.
notifyActivityDrawnForKeyguard();
}
} else {
ActivityOptions.abort(options);
}
return ActivityManager.START_TASK_TO_FRONT;
}
}
}
}
/frameworks/base/services/core/java/com/android/server/am/TaskRecord.java
final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
int numActivities = mActivities.size();
for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = mActivities.get(activityNdx);
if (r.finishing) {
continue;
}
if (r.realActivity.equals(newR.realActivity)) {
// Here it is! Now finish everything in front...
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.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear",
false)) {
--activityNdx;
--numActivities;
}
}
// Finally, if this is a normal launch mode (that is, not
// expecting onNewIntent()), then we will finish the current
// instance of the activity so a new fresh one can be started.
if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
&& (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
if (!ret.finishing) {
stack.finishActivityLocked(ret, Activity.RESULT_CANCELED, null,
"clear", false);
return null;
}
}
return ret;
}
}
return null;
}
查詢“符合要求”的Activity
插一下如何找到“符合要求”的Activity的內容。在singleTask模式下,使用findTaskLocked查詢Activity;在singleInstance模式下,使用findActivityLocked查詢Activity。
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
ActivityRecord findTaskLocked(ActivityRecord r) {
if (DEBUG_TASKS) Slog.d(TAG, "Looking for task of " + r);
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (!r.isApplicationActivity() && !stack.isHomeStack()) {
if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: (home activity) " + stack);
continue;
}
if (!stack.mActivityContainer.isEligibleForNewTasks()) {
if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: (new task not allowed) " +
stack);
continue;
}
final ActivityRecord ar = stack.findTaskLocked(r);
if (ar != null) {
return ar;
}
}
}
if (DEBUG_TASKS) Slog.d(TAG, "No task found");
return null;
}
findTaskLocked就是遍歷每個ActivityStack中的每個TaskRecord,根據TaskRecord和入參ActivityRecord的屬性決定TaskRecord中哪一個Activity符合要求。Intent#isDocument檢查一個Intent是否帶有FLAG_ACTIVITY_NEW_DOCUMENT(在API21以前是FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET),這個標誌位啟動的Activity所在的TaskRecord從後臺被帶到前臺時,會清掉這個Activity以及其上面的Activiy。 affinityIntent在android:allowTaskReparenting屬性被設定才有機會生效,否則為null。 在以下幾種情況才會視作找到了"符合要求”的Activity:1.啟動Activity的標誌位和當前遍歷到的TaskRecord均沒有設定FLAG_ACTIVITY_NEW_DOCUMENT標誌位,且TaskRecord的rootAffinity(通常是根Activity的taskAffinity)和啟動的Activity的taskAffinity相同;2.TaskRecord的Intent(通常是根Activity的Intent)的Component與啟動Activity的Intent的Component相同且TaskRecord的Intent的Data和與啟動Activity的Intent的Data相同(兩者的Intent的Data可以均為null,視為相同); 3.TaskRecord的affinityIntent不為null,affinityIntent的Component和啟動Activity的Intent的Component相同且TaskRecord的Intent的Data和與啟動Activity的Intent的Data相同(兩者的Intent的Data可以均為null,視為相同)。
/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
ActivityRecord findTaskLocked(ActivityRecord target) {
Intent intent = target.intent;//Activity啟動的Intent
ActivityInfo info = target.info;//Activity的ActivityInfo
ComponentName cls = intent.getComponent();//Intent包含的啟動元件
if (info.targetActivity != null) {
//targetActivity只在android:targetActivity被設定才生效,不走此分支
cls = new ComponentName(info.packageName, info.targetActivity);
}
final int userId = UserHandle.getUserId(info.applicationInfo.uid);
boolean isDocument = intent != null & intent.isDocument();
// If documentData is non-null then it must match the existing task data.
Uri documentData = isDocument ? intent.getData() : null;
if (DEBUG_TASKS) Slog.d(TAG, "Looking for task of " + target + " in " + this);
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
//遍歷一個ActivityStack中的所有TaskRecord
final TaskRecord task = mTaskHistory.get(taskNdx);
if (task.voiceSession != null) {
// We never match voice sessions; those always run independently.
if (DEBUG_TASKS) Slog.d(TAG, "Skipping " + task + ": voice session");
continue;
}
if (task.userId != userId) {
// Looking for a different task.
if (DEBUG_TASKS) Slog.d(TAG, "Skipping " + task + ": different user");
continue;
}
//得到一個TaskRecord最頂端非finish狀態的Activity
final ActivityRecord r = task.getTopActivity();
if (r == null || r.finishing || r.userId != userId ||
r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
if (DEBUG_TASKS) Slog.d(TAG, "Skipping " + task + ": mismatch root " + r);
continue;
}
final Intent taskIntent = task.intent;//TaskRecord的根Activity的Intent
final Intent affinityIntent = task.affinityIntent;
final boolean taskIsDocument;
final Uri taskDocumentData;
if (taskIntent != null && taskIntent.isDocument()) {
taskIsDocument = true;
taskDocumentData = taskIntent.getData();
} else if (affinityIntent != null && affinityIntent.isDocument()) {
taskIsDocument = true;
taskDocumentData = affinityIntent.getData();
} else {
taskIsDocument = false;
taskDocumentData = null;
}
if (DEBUG_TASKS) Slog.d(TAG, "Comparing existing cls="
+ taskIntent.getComponent().flattenToShortString()
+ "/aff=" + r.task.rootAffinity + " to new cls="
+ intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
if (!isDocument && !taskIsDocument && task.rootAffinity != null) {
if (task.rootAffinity.equals(target.taskAffinity)) {
if (DEBUG_TASKS) Slog.d(TAG, "Found matching affinity!");
return r;
}
} else if (taskIntent != null && taskIntent.getComponent() != null &&
taskIntent.getComponent().compareTo(cls) == 0 &&
Objects.equals(documentData, taskDocumentData)) {
if (DEBUG_TASKS) Slog.d(TAG, "Found matching class!");
//dump();
if (DEBUG_TASKS) Slog.d(TAG, "For Intent " + intent + " bringing to top: "
+ r.intent);
return r;
} else if (affinityIntent != null && affinityIntent.getComponent() != null &&
affinityIntent.getComponent().compareTo(cls) == 0 &&
Objects.equals(documentData, taskDocumentData)) {
if (DEBUG_TASKS) Slog.d(TAG, "Found matching class!");
//dump();
if (DEBUG_TASKS) Slog.d(TAG, "For Intent " + intent + " bringing to top: "
+ r.intent);
return r;
} else if (DEBUG_TASKS) {
Slog.d(TAG, "Not a match: " + task);
}
}
return null;
}
再看看singleInstance模式下使用的findActivityLocked函式。findActivityLocked不會再以Affinity相關屬性作為查詢依據,而是簡單地遍歷所有ActivityStack的TaskRecord,直到找到一個不是finish狀態且其Intent的Component和啟動Activity的Intent的Component相同且其userId和要啟動的Activity的userId相同的Activity。也就是說,singleInstance模式下,只有在找到一個已存在的一模一樣的Activity才會不新建一個TaskRecord,否則都會建立新的TaskRecord。
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityRecord ar = stacks.get(stackNdx).findActivityLocked(intent, info);
if (ar != null) {
return ar;
}
}
}
return null;
}
/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
ComponentName cls = intent.getComponent();
if (info.targetActivity != null) {
cls = new ComponentName(info.packageName, info.targetActivity);
}
final int userId = UserHandle.getUserId(info.applicationInfo.uid);
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
TaskRecord task = mTaskHistory.get(taskNdx);
if (!isCurrentProfileLocked(task.userId)) {
return null;
}
final ArrayList<ActivityRecord> activities = task.mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = activities.get(activityNdx);
if (!r.finishing && r.intent.getComponent().equals(cls) && r.userId == userId) {
//Slog.i(TAG, "Found matching class!");
//dump();
//Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
return r;
}
}
}
return null;
}
taskAffinity屬性
taskAffinity可以通過android:taskAffinity在AndroidManifest.xml的Application標籤中指定,表示這個Application內所有的Activity都帶上這個taskAffinity;也可以通過在Activity標籤中設定android:taskAffinity屬性單獨為一個Activity指定taskAffinity。 下面是解析Application標籤的過程。
/frameworks/base/core/java/android/content/pm/PackageParser.java
private boolean parseBaseApplication(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
throws XmlPullParserException, IOException {
...
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_allowTaskReparenting,
false)) {
ai.flags |= ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING;
}
...
if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) {
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity,
Configuration.NATIVE_CONFIG_VERSION);
} else {
// Some older apps have been seen to use a resource reference
// here that on older builds was ignored (with a warning). We
// need to continue to do this for them so they don't break.
str = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity);
}
ai.taskAffinity = buildTaskAffinityName(ai.packageName, ai.packageName,
str, outError);
下面是解析Activity標籤的過程。
/frameworks/base/core/java/android/content/pm/PackageParser.java
private Activity parseActivity(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, int flags, String[] outError,
boolean receiver, boolean hardwareAccelerated)
throws XmlPullParserException, IOException {
...
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestActivity_taskAffinity,
Configuration.NATIVE_CONFIG_VERSION);
a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName,
owner.applicationInfo.taskAffinity, str, outError);
...
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_allowTaskReparenting,
(owner.applicationInfo.flags&ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING) != 0)) {
a.info.flags |= ActivityInfo.FLAG_ALLOW_TASK_REPARENTING;
}
通過buildTaskAffinityName來構造一個合適的taskAffinity。呼叫parseBaseApplication時,第一個引數為應用的包名,第二個引數為預設的taskAffinity,第三個引數為通過android:taskAffinity指定的值。 parseBaseApplication中buildTaskAffinityName的第一和第二個引數均是應用包名,若沒有指定android:taskAffinity或者android:taskAffinity為空字串,直接將應用包名作為taskAffinity。若指定了android:taskAffinity,則通過buildCompoundName構造一個合適的taskAffinity:1.若android:taskAffinity的值以“:”開頭,則taskAffinity為應用包名和android:taskAffinity的值去掉“:”後連線起來的字串。2.若android:taskAffinity的值不以“:”開頭,則taskAffinity就是android:taskAffinity的值。 parseActivity中buildTaskAffinityName的第一個引數是應用包名,第二個引數是在parseBaseApplication中獲得的taskAffinity值,第三個引數是android:taskAffinity指定的值。依樣畫葫蘆,可以知道:taskAffinity的獲取首先是看Activity標籤指定的android:taskAffinity的值,再看Application標籤指定的android:taskAffinity的值,最後再以包名作為taskAffinity的值,優先順序從高到低。
/frameworks/base/core/java/android/content/pm/PackageParser.java
private static String buildTaskAffinityName(String pkg, String defProc,
CharSequence procSeq, String[] outError) {
if (procSeq == null) {
return defProc;
}
if (procSeq.length() <= 0) {
return null;
}
return buildCompoundName(pkg, procSeq, "taskAffinity", outError);
}
/frameworks/base/core/java/android/content/pm/PackageParser.java
private static String buildCompoundName(String pkg,
CharSequence procSeq, String type, String[] outError) {
String proc = procSeq.toString();
char c = proc.charAt(0);
if (pkg != null && c == ':') {
if (proc.length() < 2) {
outError[0] = "Bad " + type + " name " + proc + " in package " + pkg
+ ": must be at least two characters";
return null;
}
String subName = proc.substring(1);
String nameError = validateName(subName, false);
if (nameError != null) {
outError[0] = "Invalid " + type + " name " + proc + " in package "
+ pkg + ": " + nameError;
return null;
}
return (pkg + proc).intern();
}
String nameError = validateName(proc, true);
if (nameError != null && !"system".equals(proc)) {
outError[0] = "Invalid " + type + " name " + proc + " in package "
+ pkg + ": " + nameError;
return null;
}
return proc.intern();
啟動過程
下面這部分是處理singleTop模式或者帶有FLAG_ACTIVITY_SINGLE_TOP啟動標誌位的情況。如果當前最頂端的TaskRecord的最頂端的Activity的元件名和要啟動的Activity的元件名和userId相同,則使用resumeTopActivitiesLocked來resume這個已存在的Activity,然後呼叫Activity的onNewIntent方法後返回。
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
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.
ActivityStack topStack = getFocusedStack();
ActivityRecord top = topStack.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
|| launchSingleTop || launchSingleTask) {
ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top,
top.task);
// For paranoia, make sure we have correctly
// resumed the top activity.
topStack.mLastPausedActivity = null;
if (doResume) {
resumeTopActivitiesLocked();
}
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, r.launchedFromPackage);
return ActivityManager.START_DELIVERED_TO_TOP;
}
}
}
}
} else {
if (r.resultTo != null) {
r.resultTo.task.stack.sendActivityResultLocked(-1, r.resultTo, r.resultWho,
r.requestCode, Activity.RESULT_CANCELED, null);
}
ActivityOptions.abort(options);
return ActivityManager.START_CLASS_NOT_FOUND;
}
下面是建立一個新的TaskRecord的流程。當啟動的Activity不需要返回結果給啟動發起者(resultTo為null)且沒有指定啟動的TaskRecord(inTask為null)且不需要在已存在的TaskRecord新增要啟動的Activity(addingToTask為false)且啟動標誌位帶有FLAG_ACTIVITY_NEW_TASK時,進入此流程。 adjustStackFocus用來獲取一個合適的ActivityStack。reuseTask表示是否複用一個清空過的TaskRecord(與FLAG_ACTIVITY_CLEAR_TASK標誌位有關,此處權當為null)。createTaskRecord用來建立一個新的TaskRecord。
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
// Should this be considered a new task?
if (r.resultTo == null && inTask == null && !addingToTask
&& (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
if (isLockTaskModeViolation(reuseTask)) {
Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
newTask = true;
targetStack = adjustStackFocus(r, newTask);
if (!launchTaskBehind) {
targetStack.moveToFront("startingNewTask");
}
if (reuseTask == null) {
r.setTask(targetStack.createTaskRecord(getNextTaskId(),
newTaskInfo != null ? newTaskInfo : r.info,
newTaskIntent != null ? newTaskIntent : intent,
voiceSession, voiceInteractor, !launchTaskBehind /* toTop */),
taskToAffiliate);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " +
r.task);
} else {
r.setTask(reuseTask, taskToAffiliate);
}
if (!movedHome) {
if ((launchFlags &
(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME))
== (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) {
// Caller wants to appear on home activity, so before starting
// their own activity we will bring home to the front.
r.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
}
}
}
下面是預設(standard模式)的啟動流程。省去其他細節,就是把啟動的Activity的TaskRecord設為啟動發起者Activity所在的TaskRecord。
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
} else if (sourceRecord != null) {
final TaskRecord sourceTask = sourceRecord.task;
if (isLockTaskModeViolation(sourceTask)) {
Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
targetStack = sourceTask.stack;
targetStack.moveToFront("sourceStackToFront");
final TaskRecord topTask = targetStack.topTask();
if (topTask != sourceTask) {
targetStack.moveTaskToFrontLocked(sourceTask, r, options, "sourceTaskToFront");
}
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 = sourceTask.performClearTaskLocked(r, launchFlags);
keepCurTransition = true;
if (top != null) {
ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
// For paranoia, make sure we have correctly
// resumed the top activity.
targetStack.mLastPausedActivity = null;
if (doResume) {
targetStack.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.
final ActivityRecord top = sourceTask.findActivityInHistoryLocked(r);
if (top != null) {
final TaskRecord task = top.task;
task.moveActivityToFrontLocked(top);
ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, task);
top.updateOptionsLocked(options);
top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
targetStack.mLastPausedActivity = null;
if (doResume) {
targetStack.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(sourceTask, null);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in existing task " + r.task + " from source " + sourceRecord);
在末尾,通過ActivityStack#startActivityLocked來繼續下一步的啟動流程。
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
TaskRecord和ActivityStack的建立流程
在上面的流程中,我們使用了createTaskRecord和adjustStackFocus分別建立TaskRecord和獲取ActivityStack。 createTaskRecord就是new了一個TaskRecord,然後根據toTop引數決定把TaskRecord放在頂部還是底部。toTop為true的話,就放在頂部。上文的啟動流程就是放在頂部的。
/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
boolean toTop) {
TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession,
voiceInteractor);
addTask(task, toTop, false);
return task;
}
TaskRecord建構函式就是初始化一些變數。尤其在setIntent中初始化的成員變數,包括affinity,rootAffinity,intent,realActivity等,分別被初始化為根Activity的taskAffinity,根Activity的taskAffinity,根Activity的intent,根Activity的元件。
/frameworks/base/services/core/java/com/android/server/am/TaskRecord.java
TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename);
taskId = _taskId;
mAffiliatedTaskId = _taskId;
voiceSession = _voiceSession;
voiceInteractor = _voiceInteractor;
isAvailable = true;
mActivities = new ArrayList<ActivityRecord>();
setIntent(_intent, info);
}
adjustStackFocus用來分配一個合適的ActivityStack。當mLeanbackOnlyDevice為false且以下條件有一個成立:啟動的Activity是普通app的Activity(除了Launcher和com.android.systemui.recents外的Activity)或者啟動的Activity所在的TaskRecord是一個普通的TaskRecord(根Activity是普通app的Activity),才會有額外分配ActivityStack的可能。mLeanbackOnlyDevice標誌當前的裝置是否是TV裝置,通過android.software.leanback_only的feature值指定,若是TV裝置,則所有的Activity都啟動在HomeStack中,一個承載Launcher且id為0的ActivityStack。 1.若啟動的Activity已經有所在的TaskRecord了,則直接返回這個TaskRecord所在的ActivityStack。考慮到需要建立TaskRecord的情況,這個時候啟動的Activity不一定有自己的TaskRecord。2.若當前focus的ActivityStack不是HomeStack,則直接返回當前focus的ActivityStack。3.如果當前focus的ActivityStack是HomeStack,則從HomeStack的mStacks成員拿一個最頂部的非HomeStack的ActivityStack返回。4.最後才會createStackOnDisplay建立一個ActivityStack。在以上4步中,只要拿到一個合適的ActivityStack,都會更新mFocusedStack為這個合適的ActivityStack。
/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
ActivityStack adjustStackFocus(ActivityRecord r, boolean newTask) {
final TaskRecord task = r.task;
// On leanback only devices we should keep all activities in the same stack.
if (!mLeanbackOnlyDevice &&
(r.isApplicationActivity() || (task != null && task.isApplicationTask()))) {
if (task != null) {
final ActivityStack taskStack = task.stack;
if (taskStack.isOnHomeDisplay()) {
if (mFocusedStack != taskStack) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG, "adjustStackFocus: Setting " +
"focused stack to r=" + r + " task=" + task);
mFocusedStack = taskStack;
} else {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
"adjustStackFocus: Focused stack already=" + mFocusedStack);
}
}
return taskStack;
}
final ActivityContainer container = r.mInitialActivityContainer;
if (container != null) {
// The first time put it on the desired stack, after this put on task stack.
r.mInitialActivityContainer = null;
return container.mStack;
}
if (mFocusedStack != mHomeStack && (!newTask ||
mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
"adjustStackFocus: Have a focused stack=" + mFocusedStack);
return mFocusedStack;
}
final ArrayList<ActivityStack> homeDisplayStacks = mHomeStack.mStacks;
for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = homeDisplayStacks.get(stackNdx);
if (!stack.isHomeStack()) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
"adjustStackFocus: Setting focused stack=" + stack);
mFocusedStack = stack;
return mFocusedStack;
}
}
// Need to create an app stack for this user.
int stackId = createStackOnDisplay(getNextStackId(), Display.DEFAULT_DISPLAY);
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG, "adjustStackFocus: New stack r=" + r +
" stackId=" + stackId);
mFocusedStack = getStack(stackId);
return mFocusedStack;
}
return mHomeStack;
}