-
USER-NAME@MACHINE-NAME:~/Android$ emulator
点击中间的按钮,就会以"singleTask"的方式来启动SubActivity:
现在,我们如何来确认SubActivity是不是在新的任务中启动并且位于这个新任务的堆栈底部呢?Android源代码工程为我们准备了adb工具,可以查看模拟器上系统运行的状况,执行下面的命令查看;
- USER-NAME@MACHINE-NAME:~/Android$ adb shell dumpsys activity
这个命令输出的内容比较多,这里我们只关心TaskRecord部分:
- Running activities (most recent first):
- TaskRecord{4070d8f8 #3 A shy.luo.task}
- Run #2: HistoryRecord{406a13f8 shy.luo.task/.SubActivity}
- Run #1: HistoryRecord{406a0e00 shy.luo.task/.MainActivity}
- TaskRecord{4067a510 #2 A com.android.launcher}
- Run #0: HistoryRecord{40677518 com.android.launcher/com.android.launcher2.Launcher}
果然,SubActivity和MainActivity都是运行在TaskRecord#3中,并且SubActivity在MainActivity的上面。这是怎么回事呢?碰到这种情况,Linus Torvalds告诫我们:Read the fucking source code;去年张麻子又说:枪在手,跟我走;我们没有枪,但是有source code,因此,我要说:跟着代码走。
前面我们在两篇文章Android应用程序启动过程源代码分析和Android应用程序内部启动Activity过程(startActivity)的源代码分析时,分别在Step 9和Step 8中分析了Activity在启动过程中与任务相关的函数ActivityStack.startActivityUncheckedLocked函数中,它定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:
- public class ActivityStack {
- ......
- final int startActivityUncheckedLocked(ActivityRecord r,
- ActivityRecord sourceRecord, Uri[] grantedUriPermissions,
- int grantedMode, boolean onlyIfNeeded, boolean doResume) {
- final Intent intent = r.intent;
- final int callingUid = r.launchedFromUid;
- int launchFlags = intent.getFlags();
- ......
- ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
- != 0 ? r : null;
- ......
- if (sourceRecord == null) {
- ......
- } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- ......
- } 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;
- }
- ......
- boolean addingToTask = false;
- 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) {
- // If bring to front is requested, and no result is requested, and
- // we can find a task that was started with this same
- // component, then instead of launching bring that one to the front.
- if (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.
- ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
- ? findTaskLocked(intent, r.info)
- : findActivityLocked(intent, r.info);
- if (taskTop != null) {
- ......
- 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, true);
- if (top != null) {
- ......
- } 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)) {
- ......
- } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
- ......
- } else if (!taskTop.task.rootWasReset) {
- ......
- }
- ......
- }
- }
- }
- ......
- 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)) {
- if (top.app != null && top.app.thread != null) {
- ......
- }
- }
- }
- } else {
- ......
- }
- boolean newTask = false;
- // Should this be considered a new task?
- if (r.resultTo == null && !addingToTask
- && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- // todo: should do better management of integers.
- mService.mCurTask++;
- if (mService.mCurTask <= 0) {
- mService.mCurTask = 1;
- }
- r.task = new TaskRecord(mService.mCurTask, r.info, intent,
- (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in new task " + r.task);
- newTask = true;
- if (mMainStack) {
- mService.addRecentTaskLocked(r.task);
- }
- } else if (sourceRecord != null) {
- if (!addingToTask &&
- (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
- ......
- } else if (!addingToTask &&
- (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
- ......
- }
- // 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.task = sourceRecord.task;
- ......
- } else {
- ......
- }
- ......
- startActivityLocked(r, newTask, doResume);
- return START_SUCCESS;
- }
- ......
- }
首先是获得用来启动Activity的Intent的Flags,并且保存在launchFlags变量中,这里,launcFlags的Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP位没有置位,因此,notTop为null。
接下来的这个if语句:
- if (sourceRecord == null) {
- ......
- } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- ......
- } 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;
- }
这里变量r的类型为ActivityRecord,它表示即将在启动的Activity,在这个例子中,即为SubActivity,因此,这里的r.launchMode等于ActivityInfo.LAUNCH_SINGLE_TASK,于是,无条件将launchFlags的Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP位置为1,表示这个SubActivity要在新的任务中启动,但是别急,还要看看其它条件是否满足,如果条件都满足,才可以在新的任务中启动这个SubActivity。
接下将addingToTask变量初始化为false,这个变量也将决定是否要将SubActivity在新的任务中启动,从名字我们就可以看出,默认不增加到原有的任务中启动,即要在新的任务中启动。这里的r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK条成立,条件r.resultTo == null也成立,它表这个Activity不需要将结果返回给启动它的Activity。于是会进入接下来的if语句中,执行:
- ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
- ? findTaskLocked(intent, r.info)
- : findActivityLocked(intent, r.info)
这里的条件r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE成立,于是执行findTaskLocked函数,这个函数也是定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:
- public class ActivityStack {
- ......
- /**
- * Returns the top activity in any existing task matching the given
- * Intent. Returns null if no such task is found.
- */
- 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 N = mHistory.size();
- for (int i=(N-1); i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
- if (!r.finishing && r.task != cp
- && 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;
- }
- ......
- }
这个函数无非就是根据即将要启动的SubActivity的taskAffinity属性值在系统中查找这样的一个Task:Task的affinity属性值与即将要启动的Activity的taskAffinity属性值一致。如果存在,就返回这个Task堆栈顶端的Activity回去。在上面的AndroidManifest.xml文件中,没有配置MainActivity和SubActivity的taskAffinity属性,于是它们的taskAffinity属性值就默认为父标签application的taskAffinity属性值,这里,标签application的taskAffinity也没有配置,于是它们就默认为包名,即"shy.luo.task"。由于在启动SubActivity之前,MainActivity已经启动,MainActivity启动的时候,会在一个新的任务里面启动,而这个新的任务的affinity属性就等于它的第一个Activity的taskAffinity属性值。于是,这个函数会动回表示MainActivity的ActivityRecord回去.
回到前面的startActivityUncheckedLocked函数中,这里的taskTop就表示MainActivity,它不为null,于是继续往前执行。由于条件r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK成立,于是执行下面语句:
- ActivityRecord top = performClearTaskLocked(
- kTop.task.taskId, r, launchFlags, true);
函数performClearTaskLocked也是定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:
- public class ActivityStack {
- ......
- /**
- * Perform clear operation as requested by
- * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
- * stack to the given task, then look for
- * an instance of that activity in the stack and, if found, finish all
- * activities on top of it and return the instance.
- *
- * @param newR Description of the new activity being started.
- * @return Returns the old activity that should be continue to be used,
- * or null if none was found.
- */
- private final ActivityRecord performClearTaskLocked(int taskId,
- ActivityRecord newR, int launchFlags, boolean doClear) {
- int i = mHistory.size();
- // First find the requested task.
- while (i > 0) {
- i--;
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
- if (r.task.taskId == taskId) {
- i++;
- break;
- }
- }
- // Now clear it.
- while (i > 0) {
- i--;
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
- if (r.finishing) {
- continue;
- }
- if (r.task.taskId != taskId) {
- return null;
- }
- if (r.realActivity.equals(newR.realActivity)) {
- // Here it is! Now finish everything in front...
- ActivityRecord ret = r;
- if (doClear) {
- while (i < (mHistory.size()-1)) {
- i++;
- r = (ActivityRecord)mHistory.get(i);
- if (r.finishing) {
- continue;
- }
- if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "clear")) {
- i--;
- }
- }
- }
- // 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) {
- int index = indexOfTokenLocked(ret);
- if (index >= 0) {
- finishActivityLocked(ret, index, Activity.RESULT_CANCELED,
- null, "clear");
- }
- return null;
- }
- }
- return ret;
- }
- }
- return null;
- }
- ......
- }
这个函数中作用无非就是找到ID等于参数taskId的任务,然后在这个任务中查找是否已经存在即将要启动的Activity的实例,如果存在,就会把这个Actvity实例上面直到任务堆栈顶端的Activity通过调用finishActivityLocked函数将它们结束掉。在这个例子中,就是要在属性值affinity等于"shy.luo.task"的任务中看看是否存在SubActivity类型的实例,如果有,就把它上面的Activity都结束掉。这里,属性值affinity等于"shy.luo.task"的任务只有一个MainActivity,而且它不是SubActivity的实例,所以这个函数就返回null了。