1. 程式人生 > >standard、singleTop、singleTask和singleInstance原理分析

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;
    }