【源码解析】Activity 启动模式Launch Mode机制

 Activity启动模式源码解析

 

启动模式常见应用场景

一般情况下启动activity的时候不需要指定launchMode,不指定launchMode时,使用的时默认值,默认值时standard。standard属性在运行时会被解析成FLAG_ACTIVITY_MULTIPLE_TASK,因而源码中就是用这个flag来处理standard类型的。

如果需要指定launchMode,则有两种方式,第一种方式时使用xml属性launchMode来指定,第二种方式是通过在代码中启动activity时指定flag。示例如下:

【源码解析】Activity 启动模式Launch Mode机制_第1张图片

【源码解析】Activity 启动模式Launch Mode机制_第2张图片

在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 standard in
     * the {@link android.R.attr#launchMode} attribute.
     */
    public static final int LAUNCH_MULTIPLE = 0;
    /**
     * Constant corresponding to singleTop in
     * the {@link android.R.attr#launchMode} attribute.
     */
    public static final int LAUNCH_SINGLE_TOP = 1;
    /**
     * Constant corresponding to singleTask in
     * the {@link android.R.attr#launchMode} attribute.
     */
    public static final int LAUNCH_SINGLE_TASK = 2;
    /**
     * Constant corresponding to singleInstance 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;
    /**
     * 

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哪些不会,正确的执行流程就可以掌握了,然后在仔细分析具体的复用机制就可以完全掌握了。

感兴趣的或者有问题的欢迎留言交流。

 

你可能感兴趣的:(#,android,工具,工作记录)