【源碼解析】Activity 啓動模式Launch Mode機制

 Activity啓動模式源碼解析

 

啓動模式常見應用場景

一般情況下啓動activity的時候不需要指定launchMode,不指定launchMode時,使用的時默認值,默認值時standard。standard屬性在運行時會被解析成FLAG_ACTIVITY_MULTIPLE_TASK,因而源碼中就是用這個flag來處理standard類型的。

如果需要指定launchMode,則有兩種方式,第一種方式時使用xml屬性launchMode來指定,第二種方式是通過在代碼中啓動activity時指定flag。示例如下:

在AndroidManifest中除了通過launchMode標籤設置Activity啓動模式外,還可以通過taskAffinity標籤來標識Activity所屬於的任務,用taskAffinity來給activity分組。不論使用了默認的launchMode還是指定了launchMode,都可以通過查看當前activity返回棧信息來調試。使用如下命令可以查看當前activity的返回棧信息:

adb shell dumpsys activity activities

如果啓動activity 的順序是Main -> A ->B ->C-> A (FLAG_NEW_TASK),未說明的都使用默認的launchMode,則該命令的執行結果如下:


ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
Display #0 (activities from top to bottom):
  Stack #1:
  mResumedActivity=ActivityRecord{dceffaf u0 com.anly.samples/.activity.AActivity t728}
    Task id #728
    * TaskRecord{d5cf06d #728 A=com.anly.aactivity U=0 StackId=1 sz=1}
      affinity=com.anly.aactivity
      intent={flg=0x10000000 cmp=com.anly.samples/.activity.AActivity}
      realActivity=com.anly.samples/.activity.AActivity
      * Hist #0: ActivityRecord{dceffaf u0 com.anly.samples/.activity.AActivity t728}
          packageName=com.anly.samples processName=com.anly.samples
          frontOfTask=true task=TaskRecord{d5cf06d #728 A=com.anly.aactivity U=0 StackId=1 sz=1}
          taskAffinity=com.anly.aactivity
          realActivity=com.anly.samples/.activity.AActivity
          resizeMode=RESIZE_MODE_RESIZEABLE
          realComponentName=com.anly.samples/.activity.AActivity
    Task id #727
    * TaskRecord{321c4a2 #727 A=com.anly.samples U=0 StackId=1 sz=5}
      affinity=com.anly.samples
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.anly.samples/.MainActivity}
      realActivity=com.anly.samples/.MainActivity
      * Hist #4: ActivityRecord{c127d42 u0 com.anly.samples/.activity.CActivity t727}
          taskAffinity=com.anly.samples
          realActivity=com.anly.samples/.activity.CActivity
          realComponentName=com.anly.samples/.activity.CActivity
      * Hist #3: ActivityRecord{b05e751 u0 com.anly.samples/.activity.BActivity t727}
          taskAffinity=com.anly.samples
      * Hist #2: ActivityRecord{ae468bf u0 com.anly.samples/.activity.AActivity t727}
          taskAffinity=com.anly.aactivity
      * Hist #1: ActivityRecord{dec47c7 u0 com.anly.samples/.MainActivity t727}
          taskAffinity=com.anly.samples
          realActivity=com.anly.samples/.MainActivity
          state=STOPPED stopped=true delayedResume=false finishing=false
          mActivityType=APPLICATION_ACTIVITY_TYPE
          resizeMode=RESIZE_MODE_RESIZEABLE
          realComponentName=com.anly.samples/.MainActivity

從以上返回棧信息中可以看出,整個app的返回棧對應於一個stack#1,

Display #0 (activities from top to bottom):
  Stack #1:

並且activity返回棧信息按照從上到下的方式列舉如下,有兩個task,分別爲Task id #728和Task id #727,每個task都包含一些activity。task的屬性是通過TaskRecord來描述的,有幾個重要的屬性值分別是affinity,intent和realActivity。

  • 其中affinity就對應於上面說的在xml中指定的affinity,它可以來指定特定的task id,比如這裏是#728。
  • intent指定task底部的第一個activity的,其comp屬性值同realActivity
  • realActivity指定task底部的第一個activity,該activity的taskAffinity就是該task的affinity,從這裏也可以看出來task和activity之間的各自有一些屬性相互指定
 Task id #728

    ...

 Task id #727
    ...

 值得注意的是,如果在xml中指定了taskAffinity,比如指定taskAffinity爲com.anly.aactivity,則在該示例中Task id#728中對應於AActivity,AActivity的屬性是通過ActivityRecord來描述的,它的taskAffinity就是com.anly.aactivity。同樣地,AActivity所在的task的affinity也是com.anly.aactivity。這裏是一一對應的。

* Hist #0: ActivityRecord{dceffaf u0 com.anly.samples/.activity.AActivity t728}
          packageName=com.anly.samples processName=com.anly.samples
          frontOfTask=true task=TaskRecord{d5cf06d #728 A=com.anly.aactivity U=0 StackId=1 sz=1}
          taskAffinity=com.anly.aactivity
          realActivity=com.anly.samples/.activity.AActivity
          resizeMode=RESIZE_MODE_RESIZEABLE
          realComponentName=com.anly.samples/.activity.AActivity

 如果沒有在xml中指定taskAffinity,一般這是更常見的使用場景,這種情況下,ActivityRecord中也存在taskAffinity,只不過taskAffinity使用的是默認值,爲包名。比如task id#727中的CActivity,它的taskAffinity就是com.anly.samples,和包名相同。

Task id #727
    * TaskRecord{321c4a2 #727 A=com.anly.samples U=0 StackId=1 sz=5}
      affinity=com.anly.samples
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.anly.samples/.MainActivity}
      realActivity=com.anly.samples/.MainActivity
      * Hist #4: ActivityRecord{c127d42 u0 com.anly.samples/.activity.CActivity t727}
          taskAffinity=com.anly.samples
          realActivity=com.anly.samples/.activity.CActivity
          realComponentName=com.anly.samples/.activity.CActivity

對activity返回棧信息有了總體把握之後,再去閱讀源碼,就很容易理解activity不同的啓動模式在源碼中是怎麼工作的了。

startActivityUncheckedLocked源碼解析

這裏以4.3的源碼爲例,在源碼中處理activity啓動模式的代碼在ActivityStack.java類中,處理邏輯是在startActivityUncheckedLocked這個方法中實現的。它屬於activity啓動流程中的一個環節,activity啓動流程涉及到非常多的技術點,感興趣的同學可以找相關文章閱讀。這裏重點分析startActivityUncheckedLocked方法的工作原理。

final int startActivityUncheckedLocked(ActivityRecord r,
            ActivityRecord sourceRecord, int startFlags, boolean doResume,
            Bundle options) {
        final Intent intent = r.intent;
        final int callingUid = r.launchedFromUid;

        int launchFlags = intent.getFlags();
        
        // We'll invoke onUserLeaving before onPause only if the launching
        // activity did not explicitly state that this is an automated launch.
        mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
        if (DEBUG_USER_LEAVING) Slog.v(TAG,
                "startActivity() => mUserLeaving=" + mUserLeaving);
        
        // If the caller has asked not to resume at this point, we make note
        // of this in the record so that we can skip it when trying to find
        // the top running activity.
        if (!doResume) {
            r.delayedResume = true;
        }
        
        ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
                != 0 ? r : null;

        // If the onlyIfNeeded flag is set, then we can do this if the activity
        // being launched is the same as the one making the call...  or, as
        // a special case, if we do not know the caller then we count the
        // current top activity as the caller.
        if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
            ActivityRecord checkedCaller = sourceRecord;
            if (checkedCaller == null) {
                checkedCaller = topRunningNonDelayedActivityLocked(notTop);
            }
            if (!checkedCaller.realActivity.equals(r.realActivity)) {
                // Caller is not the same as launcher, so always needed.
                startFlags &= ~ActivityManager.START_FLAG_ONLY_IF_NEEDED;
            }
        }
        ...

這段代碼重點是int launchFlags = intent.getFlags();這一句,獲取指定的flag。其餘的代碼是做一些容錯或者系統檢查,和launchMode的應用場景沒關係,略過即可。接着往下看

            
        if (sourceRecord == null) {
            // This activity is not being started from another...  in this
            // case we -always- start a new task.
            if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
                Slog.w(TAG, "startActivity called from non-Activity context; " +
                        "forcing Intent.FLAG_ACTIVITY_NEW_TASK for: "
                      + intent);
                launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
            }
        } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
            // The original activity who is starting us is running as a single
            // instance...  this new activity it is starting must go on its
            // own task.
            launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
        } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
                || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
            // The activity being started is a single instance...  it always
            // gets launched into its own task.
            launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
        }

這段代碼是爲了給 launchFlags增加FLAG_ACTIVITY_NEW_TASK。sourceRecord即啓動目標activity的源activity。源碼裏activity是通過ActivityRecord類來管理的,所以sourceRecord的類型是ActivityRecord類型。

  • 如果sourceRecord爲null,說明啓動目標activity的源activity爲空。這種情況可能是通過通知欄消息啓動,或者通過scheme協議啓動的。由於沒有上一個activity,這個activit就是第一個啓動的activity,因而需要給它分配新的task,所以要增加FLAG_ACTIVITY_NEW_TASK
  • 如果sourceRecord有值,並且sourceRecord的啓動模式是LAUNCH_SINGLE_INSTANCE。注意這裏是sourceRecord而不是目標activity,目標activity是r,它也是ActivityRecord類型。既然sourceRecord的啓動模式是LAUNCH_SINGLE_INSTANCE,根據LAUNCH_SINGLE_INSTANCE的屬性,sourceRecord對應的activity會獨佔一個task,因而需要給目標activity分配新的task,所以要增加FLAG_ACTIVITY_NEW_TASK
  • 如果目標activity,也就是r的啓動模式是LAUNCH_SINGLE_INSTANCE或者LAUNCH_SINGLE_TASK。則需要給目標activity分配新的task,所以要增加FLAG_ACTIVITY_NEW_TASK

這三種情況都需要給launchFlags增加FLAG_ACTIVITY_NEW_TASK,這樣就把launchFlags準備好了。下面就是根據launchFlags設置的flag值進行對應的處理了。

不過在這之前又是一個容錯處理,它的意思是,如果目標activity是有返回結果的,而此時目標activity還沒有啓動不應該有返回結果,所以需要把返回結果置爲null,並且返回一個RESULT_CANCELED結果。

        if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
            // For whatever reason this activity is being launched into a new
            // task...  yet the caller has requested a result back.  Well, that
            // is pretty messed up, so instead immediately send back a cancel
            // and let the new task continue launched as normal without a
            // dependency on its originator.
            Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
            sendActivityResultLocked(-1,
                    r.resultTo, r.resultWho, r.requestCode,
                Activity.RESULT_CANCELED, null);
            r.resultTo = null;
        }

下面就是根據launchFlags設置的flag值進行對應的處理了。注意launchFlags是目標activity的flag集合,而不是源activity的。


        if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
                (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
                || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
                || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {

這裏有三種情況可以進入下面的處理邏輯。

  • launchFlag包含FLAG_ACTIVITY_NEW_TASK且不包含FLAG_ACTIVITY_MULTIPLE_TASK,上文說過FLAG_ACTIVITY_MULTIPLE_TASK對應的是standard啓動模式。所以這個條件就是有FLAG_ACTIVITY_NEW_TASK屬性且不是standard。
    • 注意這個條件是個複合條件。其實寫成如下三個條件更好理解
    • FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_CLEAR_TASK
    • FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_CLEAR_TOP
    • FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_SINGLE_TOP
  • 目標activity的啓動模式是LAUNCH_SINGLE_TASK
  • 目標activity的啓動模式是LAUNCH_SINGLE_INSTANCE

這裏需要說明的是,r.launchMode的值只有四個,以常量的形式定義在ActivityInfo裏,

    /**
     * Constant corresponding to <code>standard</code> in
     * the {@link android.R.attr#launchMode} attribute.
     */
    public static final int LAUNCH_MULTIPLE = 0;
    /**
     * Constant corresponding to <code>singleTop</code> in
     * the {@link android.R.attr#launchMode} attribute.
     */
    public static final int LAUNCH_SINGLE_TOP = 1;
    /**
     * Constant corresponding to <code>singleTask</code> in
     * the {@link android.R.attr#launchMode} attribute.
     */
    public static final int LAUNCH_SINGLE_TASK = 2;
    /**
     * Constant corresponding to <code>singleInstance</code> in
     * the {@link android.R.attr#launchMode} attribute.
     */
    public static final int LAUNCH_SINGLE_INSTANCE = 3;

與此對應的是Intent的FLAG常量值,他們定義在Intent類中,常用的常量值有

    /**
     * If set, the activity will not be launched if it is already running
     * at the top of the history stack.
     */
    public static final int FLAG_ACTIVITY_SINGLE_TOP = 0x20000000;
    /**
     * If set, this activity will become the start of a new task on this
     * history stack.  A task (from the activity that started it to the
     * next task activity) defines an atomic group of activities that the
     * user can move to.  Tasks can be moved to the foreground and background;
     * all of the activities inside of a particular task always remain in
     * the same order.  
     */
    public static final int FLAG_ACTIVITY_NEW_TASK = 0x10000000;
    /**
     * <p>This flag is ignored if
     * {@link #FLAG_ACTIVITY_NEW_TASK} is not set.
     */
    public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 0x08000000;
    /**
     * If set, and the activity being launched is already running in the
     * current task, then instead of launching a new instance of that activity,
     * all of the other activities on top of it will be closed and this Intent
     * will be delivered to the (now on top) old activity as a new Intent.
     */
    public static final int FLAG_ACTIVITY_CLEAR_TOP = 0x04000000;

雖然Intent的FLAG也有四個,但是他們並不是和ActivityInfo中的值一一對應的。只有SingleTop和Standard是兩者共有的。

回到上面說的三種條件,如果滿足任何一種條件,就會進入後續邏輯,執行下面的操作。

// See if there is a task to bring to the front.  If this is
// a SINGLE_INSTANCE activity, there can be one and only one
// instance of it in the history, and it is always in its own
// unique task, so we do a special search.
ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
                        ? findTaskLocked(intent, r.info)
                        : findActivityLocked(intent, r.info);

這個邏輯非常重要,網上很多介紹launchMode的文章之所以講不清楚甚至講解錯誤就是因爲這段代碼看錯了。

這裏拿目標activity的launchMode去對比,如果不是LAUNCH_SINGLE_INSTANCE,則走findTaskLocked方法,如果是LAUNCH_SINGLE_INSTANCE則走findActivityLocked方法。

理解這段代碼有個小技巧,想一下上文講的返回棧信息,一個stack對應多個task,一個task對應多個activity的結構。用表格簡單描述這種數據結構如下

返回棧結構
  task #728 activityA0、activityB0、activityC0、...
stack task#727 activityA1、activityB1、activityC1、...
  task#726 activityA2、activityB2、activityC2、...

 

 

 

 

這裏判斷的時候,如果不是singleInstance模式,就走findTaskLocked,意思是遍歷某個特定task裏的activity,比如遍歷task#728的activityA0、activityB0、activityC0、...

如果是singleInstance模式,則走findActivityLocked,意思是遍歷所有task裏的所有activity,這種情況會遍歷

  • task#728的activityA0、activityB0、activityC0
  • task#727的activityA1、activityB1、activityC1
  • task#726的activityA2、activityB2、activityC2

findTaskLocked源碼如下,

private ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {
        ComponentName cls = intent.getComponent();
        if (info.targetActivity != null) {
            cls = new ComponentName(info.packageName, info.targetActivity);
        }

        TaskRecord cp = null;

        final int userId = UserHandle.getUserId(info.applicationInfo.uid);
        final int N = mHistory.size();
        for (int i=(N-1); i>=0; i--) {
            ActivityRecord r = mHistory.get(i);
            if (!r.finishing && r.task != cp && r.userId == userId
                    && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
                cp = r.task;
                //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()
                //        + "/aff=" + r.task.affinity + " to new cls="
                //        + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity);
                if (r.task.affinity != null) {
                    if (r.task.affinity.equals(info.taskAffinity)) {
                        //Slog.i(TAG, "Found matching affinity!");
                        return r;
                    }
                } else if (r.task.intent != null
                        && r.task.intent.getComponent().equals(cls)) {
                    //Slog.i(TAG, "Found matching class!");
                    //dump();
                    //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
                    return r;
                } else if (r.task.affinityIntent != null
                        && r.task.affinityIntent.getComponent().equals(cls)) {
                    //Slog.i(TAG, "Found matching class!");
                    //dump();
                    //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
                    return r;
                }
            }
        }

        return null;
    }

它是從N-1開始向前遍歷的,符合棧後進先出的特性。並且在for循環裏最外層的if條件中有 r.task != cp的判斷,這個判斷就可以保證找到特定的task。並且這裏返回的是目標task頂端第一個activity。需要注意的是,雖然在這裏從N-1開始稱之爲從後向前遍歷,但是從用戶角度看,N-1是返回棧最頂部的activity,0是最底部的activity,是被其他activity蓋住的。也就是這裏說的“後”,對應於視覺上最頂部的activity。

findActivityLocked源碼如下

    /**
     * Returns the first activity (starting from the top of the stack) that
     * is the same as the given activity.  Returns null if no such activity
     * is found.
     */
    private ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
        ComponentName cls = intent.getComponent();
        if (info.targetActivity != null) {
            cls = new ComponentName(info.packageName, info.targetActivity);
        }

        final int N = mHistory.size();
        for (int i=(N-1); i>=0; i--) {
            ActivityRecord r = (ActivityRecord)mHistory.get(i);
            if (!r.finishing) {
                if (r.intent.getComponent().equals(cls)) {
                    //Slog.i(TAG, "Found matching class!");
                    //dump();
                    //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
                    return r;
                }
            }
        }

        return null;
    }

它不區分task,是從後向前遍歷的整個mHistory,而mHistory對應的就是activity返回棧信息中的stack。因而這裏也就清楚singleInstance的工作原理了,他是整個app的stack裏唯一的,這也就是爲什麼稱之爲單例的原因。與此對比,singleTask指的是特定task內唯一的,不同task內可以不唯一。這就是兩者的區別。

繼續往下看

                if (taskTop != null) {
                    if (taskTop.task.intent == null) {
                        // This task was started because of movement of
                        // the activity based on affinity...  now that we
                        // are actually launching it, we can assign the
                        // base intent.
                        taskTop.task.setIntent(intent, r.info);
                        // 727 A
                    }
                    // If the target task is not in the front, then we need
                    // to bring it to the front...  except...  well, with
                    // SINGLE_TASK_LAUNCH it's not entirely clear.  We'd like
                    // to have the same behavior as if a new instance was
                    // being started, which means not bringing it to the front
                    // if the caller is not itself in the front.
                    ActivityRecord curTop = topRunningNonDelayedActivityLocked(notTop);
                    // AAcitivty=curTop 728 AAcitivty
                    if (curTop != null && curTop.task != taskTop.task) {
                        r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
                        boolean callerAtFront = sourceRecord == null
                                || curTop.task == sourceRecord.task;
                        if (callerAtFront) {
                            // We really do want to push this one into the
                            // user's face, right now.
                            movedHome = true;
                            moveHomeToFrontFromLaunchLocked(launchFlags);
                            moveTaskToFrontLocked(taskTop.task, r, options);
                            options = null;
                        }
                    }
                    // If the caller has requested that the target task be
                    // reset, then do so.
                    if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
                        taskTop = resetTaskIfNeededLocked(taskTop, r);
                    }
                    if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED)  != 0) {
                        // We don't need to start a new activity, and
                        // the client said not to do anything if that
                        // is the case, so this is it!  And for paranoia, make
                        // sure we have correctly resumed the top activity.
                        if (doResume) {
                            resumeTopActivityLocked(null, options);
                        } else {
                            ActivityOptions.abort(options);
                        }
                        return ActivityManager.START_RETURN_INTENT_TO_CALLER;
                    }

                    if ((launchFlags &
                            (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK))
                            == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK)) {
                        // The caller has requested to completely replace any
                        // existing task with its new activity.  Well that should
                        // not be too hard...
                        reuseTask = taskTop.task;
                        performClearTaskLocked(taskTop.task.taskId);
                        reuseTask.setIntent(r.intent, r.info);
                    } else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
                        // In this situation we want to remove all activities
                        // from the task up to the one being started.  In most
                        // cases this means we are resetting the task to its
                        // initial state.
                        ActivityRecord top = performClearTaskLocked(
                                taskTop.task.taskId, r, launchFlags);
                        if (top != null) {
                            if (top.frontOfTask) {
                                // Activity aliases may mean we use different
                                // intents for the top activity, so make sure
                                // the task now has the identity of the new
                                // intent.
                                top.task.setIntent(r.intent, r.info);
                            }
                            logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
                            top.deliverNewIntentLocked(callingUid, r.intent);
                        } else {
                            // A special case: we need to
                            // start the activity because it is not currently
                            // running, and the caller has asked to clear the
                            // current task to have this activity at the top.
                            addingToTask = true;
                            // Now pretend like this activity is being started
                            // by the top of its task, so it is put in the
                            // right place.
                            sourceRecord = taskTop;
                        }
                    } else if (r.realActivity.equals(taskTop.task.realActivity)) {
                        // In this case the top activity on the task is the
                        // same as the one being launched, so we take that
                        // as a request to bring the task to the foreground.
                        // If the top activity in the task is the root
                        // activity, deliver this new intent to it if it
                        // desires.
                        if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
                                || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP)
                                && taskTop.realActivity.equals(r.realActivity)) {
                            logStartActivity(EventLogTags.AM_NEW_INTENT, r, taskTop.task);
                            if (taskTop.frontOfTask) {
                                taskTop.task.setIntent(r.intent, r.info);
                            }
                            taskTop.deliverNewIntentLocked(callingUid, r.intent);
                        } else if (!r.intent.filterEquals(taskTop.task.intent)) {
                            // In this case we are launching the root activity
                            // of the task, but with a different intent.  We
                            // should start a new instance on top.
                            addingToTask = true;
                            sourceRecord = taskTop;
                        }
                    } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
                        // In this case an activity is being launched in to an
                        // existing task, without resetting that task.  This
                        // is typically the situation of launching an activity
                        // from a notification or shortcut.  We want to place
                        // the new activity on top of the current task.
                        addingToTask = true;
                        sourceRecord = taskTop;
                    } else if (!taskTop.task.rootWasReset) {
                        // In this case we are launching in to an existing task
                        // that has not yet been started from its front door.
                        // The current task has been brought to the front.
                        // Ideally, we'd probably like to place this new task
                        // at the bottom of its stack, but that's a little hard
                        // to do with the current organization of the code so
                        // for now we'll just drop it.
                        taskTop.task.setIntent(r.intent, r.info);
                    }
                    // 不是NEW_TASK,也不是CLEAR_TASK
                    // 且找到了那個目標activity,不論是singleTask,singleInstance還是singleTop
                    if (!addingToTask && reuseTask == null) {
                        // We didn't do anything...  but it was needed (a.k.a., client
                        // don't use that intent!)  And for paranoia, make
                        // sure we have correctly resumed the top activity.
                        if (doResume) {
                            resumeTopActivityLocked(null, options);
                        } else {
                            ActivityOptions.abort(options);
                        }
                        return ActivityManager.START_TASK_TO_FRONT;
                    }
                }

如果找到了taskTop,則進入喚醒邏輯,否則創建新的。喚醒邏輯裏有兩個因素會影響到具體執行流程,一個是剛纔找到的要被喚醒的taskTop,一個是當前返回棧棧頂的activity,源碼裏稱之爲curTop。

  • 如果taskTop和curTop各自所屬的task不同,則把taskTop所屬的task移動到頂部,讓該task可見。具體代碼是moveTaskToFrontLocked方法。如果相同則不需要移動。執行到這裏就可以保證目標activity所在的task已經處於前臺可見。
  • 接下來看目標activity的launchFlags,如果有FLAG_ACTIVITY_CLEAR_TASK,則把同一task內的所有activity都clear掉,具體代碼是performClearTaskLocked(taskTop.task.taskId)。此時目標activity等待後續喚醒,task被標記爲複用。
  • 如果目標activity的launchFlags包含有FLAG_ACTIVITY_CLEAR_TOP,或者launchMode是LAUNCH_SIGNLE_TASK||LAUNCH_SIGNLE_INSTANCE,則把同一task內的它上方的所有activity都clear掉,具體代碼是performClearTaskLocked(taskTop.task.taskId, r, launchFlags)。注意區分performClearTaskLocked這兩個方法,這是三個參數的重載方法,上面是一個參數的。此時通過deliverNewIntentLocked調用目標activity的onNewIntent方法。
  • 如果目標activity的launchFlags包含有FLAG_ACTIVITY_SINGLE_TOP或者launchMode是LAUNCH_SINGLE_TOP,此時通過deliverNewIntentLocked調用目標activity的onNewIntent方法。
  • 最後執行resumeTopActivityLocked,該方法回調用目標activity的onResume方法

這裏就處理完了上文三種情況。爲了方便對比這裏再次羅列下那三種情況。

  • launchFlag包含FLAG_ACTIVITY_NEW_TASK且不包含FLAG_ACTIVITY_MULTIPLE_TASK,上文說過FLAG_ACTIVITY_MULTIPLE_TASK對應的是standard啓動模式。所以這個條件就是有FLAG_ACTIVITY_NEW_TASK屬性且不是standard。注意這個條件是個複合條件。其實寫成如下三個條件更好理解。
    • FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_CLEAR_TASK
    • FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_CLEAR_TOP
    • FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_SINGLE_TOP
  • 目標activity的啓動模式是LAUNCH_SINGLE_TASK
  • 目標activity的啓動模式是LAUNCH_SINGLE_INSTANCE

那這三種情況處理完之後,可以看到除了FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_CLEAR_TASK的情況return之外,其餘情況並沒有return而是繼續向下執行。繼續向下執行的情況有

  • FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_SINGLE_TOP
  • FLAG_ACTIVITY_NEW_TASK && FLAG_ACTIVITY_CLEAR_TASK
  • LAUNCH_MULTIPLE
  • LAUNCH_SINGLE_TOP
  • LAUNCH_SINGLE_TASK
  • LAUNCH_SINGLE_INSTANCE
  • FLAG_ACTIVITY_MULTIPLE_TASK
  • FLAG_ACTIVITY_CLEAR_TASK
  • FLAG_ACTIVITY_CLEAR_TOP
  • FLAG_ACTIVITY_SINGLE_TOP

這些情況在執行過程中有些情況的組合會被處理且提前return,比如FLAG_ACTIVITY_SINGLE_TOP,LAUNCH_SINGLE_TOP和LAUNCH_SINGLE_TASK。不滿足提前return條件的則繼續向下執行,

        if (r.packageName != null) {
            // If the activity being launched is the same as the one currently
            // at the top, then we need to check if it should only be launched
            // once.
            ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
            if (top != null && r.resultTo == null) {
                if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
                    if (top.app != null && top.app.thread != null) {
                        if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
                            logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
                            // For paranoia, make sure we have correctly
                            // resumed the top activity.
                            if (doResume) {
                                resumeTopActivityLocked(null);
                            }
                            ActivityOptions.abort(options);
                            if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
                                // We don't need to start a new activity, and
                                // the client said not to do anything if that
                                // is the case, so this is it!
                                return ActivityManager.START_RETURN_INTENT_TO_CALLER;
                            }
                            top.deliverNewIntentLocked(callingUid, r.intent);
                            return ActivityManager.START_DELIVERED_TO_TOP;
                        }
                    }
                }
            }

        } else {
            if (r.resultTo != null) {
                sendActivityResultLocked(-1,
                        r.resultTo, r.resultWho, r.requestCode,
                    Activity.RESULT_CANCELED, null);
            }
            ActivityOptions.abort(options);
            return ActivityManager.START_CLASS_NOT_FOUND;
        }

先拿到當前前臺可見的activity這裏命名爲top,如果top非空,且top.realActivity和目標r.realActivity一致,則繼續判斷是否有FLAG_ACTIVITY_SINGLE_TOP或者FLAG_ACTIVITY_SINGLE_TASK,LAUNCH_SINGLE_TASK。如果滿足條件則通過deliverNewIntentLocked調用目標activity的onNewIntent方法。

else分支是容錯處理,略過,繼續往下看。

        // Should this be considered a new task?
        if (r.resultTo == null && !addingToTask
                && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
            if (reuseTask == null) {
                // todo: should do better management of integers.
                mService.mCurTask++;
                if (mService.mCurTask <= 0) {
                    mService.mCurTask = 1;
                }
                r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true);
                if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                        + " in new task " + r.task);
            } else {
                r.setTask(reuseTask, reuseTask, true);
            }
            newTask = true;
            if (!movedHome) {
                moveHomeToFrontFromLaunchLocked(launchFlags);
            }
            
        } else if (sourceRecord != null) {
            if (!addingToTask &&
                    (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
                // In this case, we are adding the activity to an existing
                // task, but the caller has asked to clear that task if the
                // activity is already running.
                ActivityRecord top = performClearTaskLocked(
                        sourceRecord.task.taskId, r, launchFlags);
                keepCurTransition = true;
                if (top != null) {
                    logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
                    top.deliverNewIntentLocked(callingUid, r.intent);
                    // For paranoia, make sure we have correctly
                    // resumed the top activity.
                    if (doResume) {
                        resumeTopActivityLocked(null);
                    }
                    ActivityOptions.abort(options);
                    return ActivityManager.START_DELIVERED_TO_TOP;
                }
            } else if (!addingToTask &&
                    (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
                // In this case, we are launching an activity in our own task
                // that may already be running somewhere in the history, and
                // we want to shuffle it to the front of the stack if so.
                int where = findActivityInHistoryLocked(r, sourceRecord.task.taskId);
                if (where >= 0) {
                    ActivityRecord top = moveActivityToFrontLocked(where);
                    logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
                    top.updateOptionsLocked(options);
                    top.deliverNewIntentLocked(callingUid, r.intent);
                    if (doResume) {
                        resumeTopActivityLocked(null);
                    }
                    return ActivityManager.START_DELIVERED_TO_TOP;
                }
            }
            // An existing activity is starting this new activity, so we want
            // to keep the new one in the same task as the one that is starting
            // it.
            r.setTask(sourceRecord.task, sourceRecord.thumbHolder, false);
            if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                    + " in existing task " + r.task);

        } else {
            // This not being started from an existing activity, and not part
            // of a new task...  just put it in the top task, though these days
            // this case should never happen.
            final int N = mHistory.size();
            ActivityRecord prev =
                N > 0 ? mHistory.get(N-1) : null;
            r.setTask(prev != null
                    ? prev.task
                    : new TaskRecord(mService.mCurTask, r.info, intent), null, true);
            if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                    + " in new guessed " + r.task);
        }

一般情況下,都是用默認值或者僅僅指定某一個啓動屬性,這時都會走這塊邏輯。具體是

  • 如果指定了目標activity的FALG_ACTIVITY_NEW_TASK,且在上面的邏輯處理中找到了可以複用的目標activity,則進入詞條件。但是這裏並沒有return。而是繼續向下執行,直到調用startActivityLocked結束。
    • 若無可以複用的task,則走if(reuseTask == null)的邏輯。mCurTask++且new一個新的TaskRecord。
    • 若有,則複用reuseTask。
  • 否則,檢測是否指定了FLAG_ACTIVITY_CLEAR_TOP,如果指定了則走clearTop 的邏輯,同上。這裏會return。
  • 否則,檢測是否指定了FLAG_ACTIVITY_REORDER_TO_FRONT,一般開發者不會指定這個,略
  • 如果均未指定,這種情況下就是默認值(standard)的情況則直接複用父類的task
    // An existing activity is starting this new activity, so we want
    // to keep the new one in the same task as the one that is starting
    // it.
    r.setTask(sourceRecord.task, sourceRecord.thumbHolder, false);

    繼續執行,通過startActivityLocked來啓動一個新的activity。

    startActivityLocked(r, newTask, doResume, keepCurTransition, options);

     

  • 否則,就如註釋所說these days this case should never happen.略。

至此,所以流程處理完畢。

總結一下,理解launchMode,需要現在腦海中建立那個activity返回棧信息從而有個總體印象,然後再去閱讀代碼。代碼分成兩大部分,分別對應上文說的三種情況以及後續的情況。這些情況有些在執行時就提前return完成了處理,有些則不會return繼續向下執行直到方法結束。這也是整體代碼比較難懂的原因。只要梳理清楚了那些條件會return哪些不會,正確的執行流程就可以掌握了,然後在仔細分析具體的複用機制就可以完全掌握了。

感興趣的或者有問題的歡迎留言交流。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章