Android 重学系列 Activity的启动流程(二)

正文

如果遇到错误,请在本文指出:https://www.jianshu.com/p/4d34de4418e0

上篇,讲述的在正式启动前,做了权限判断,再准备ActivityRecord,本文将介绍在Activity启动中,Activity的栈的变化。

startActivityUnchecked 初步处理Activity的栈

文件:/frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java

在这个方法中,就是面试常问的启动模式,几种模式混搭在一次,在栈内的情况。

这里我分为7个步骤来详细剖析Activity栈的算法。

初始化计算Activity栈

    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
            ActivityRecord[] outActivity) {

        setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                voiceInteractor);


        computeLaunchingTaskFlags();

        computeSourceStack();

        mIntent.setFlags(mLaunchFlags);

        ActivityRecord reusedActivity = getReusableIntentActivity();

setInitialState初始化如下的数据

 private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
            boolean doResume, int startFlags, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
        reset(false /* clearRequest */);

        mStartActivity = r;
        mIntent = r.intent;
        mOptions = options;
        mCallingUid = r.launchedFromUid;
        mSourceRecord = sourceRecord;
        mVoiceSession = voiceSession;
        mVoiceInteractor = voiceInteractor;
//获取一个优先的逻辑显示器,是否是vr模式(相关源码会在之后说DisplayService)
        mPreferredDisplayId = getPreferedDisplayId(mSourceRecord, mStartActivity, options);

        mLaunchParams.reset();

        mSupervisor.getLaunchParamsController().calculate(inTask, null /*layout*/, r, sourceRecord,
                options, mLaunchParams);

        mLaunchMode = r.launchMode;

        mLaunchFlags = adjustLaunchFlagsToDocumentMode(
                r, LAUNCH_SINGLE_INSTANCE == mLaunchMode,
                LAUNCH_SINGLE_TASK == mLaunchMode, mIntent.getFlags());
        mLaunchTaskBehind = r.mLaunchTaskBehind
                && !isLaunchModeOneOf(LAUNCH_SINGLE_TASK, LAUNCH_SINGLE_INSTANCE)
                && (mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0;

        sendNewTaskResultRequestIfNeeded();

        if ((mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
            mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
        }

        // If we are actually going to launch in to a new task, there are some cases where
        // we further want to do multiple task.
        if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
            if (mLaunchTaskBehind
                    || r.info.documentLaunchMode == DOCUMENT_LAUNCH_ALWAYS) {
                mLaunchFlags |= FLAG_ACTIVITY_MULTIPLE_TASK;
            }
        }

        // We'll invoke onUserLeaving before onPause only if the launching
        // activity did not explicitly state that this is an automated launch.
        mSupervisor.mUserLeaving = (mLaunchFlags & FLAG_ACTIVITY_NO_USER_ACTION) == 0;
        if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
                "startActivity() => mUserLeaving=" + mSupervisor.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.
        mDoResume = doResume;
        if (!doResume || !r.okToShowLocked()) {
            r.delayedResume = true;
            mDoResume = false;
        }

        if (mOptions != null) {
            if (mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) {
                r.mTaskOverlay = true;
                if (!mOptions.canTaskOverlayResume()) {
                    final TaskRecord task = mSupervisor.anyTaskForIdLocked(
                            mOptions.getLaunchTaskId());
                    final ActivityRecord top = task != null ? task.getTopActivity() : null;
                    if (top != null && !top.isState(RESUMED)) {

                        // The caller specifies that we'd like to be avoided to be moved to the
                        // front, so be it!
                        mDoResume = false;
                        mAvoidMoveToFront = true;
                    }
                }
            } else if (mOptions.getAvoidMoveToFront()) {
                mDoResume = false;
                mAvoidMoveToFront = true;
            }
        }

        mNotTop = (mLaunchFlags & FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;

        mInTask = inTask;
        // In some flows in to this function, we retrieve the task record and hold on to it
        // without a lock before calling back in to here...  so the task at this point may
        // not actually be in recents.  Check for that, and if it isn't in recents just
        // consider it invalid.
        if (inTask != null && !inTask.inRecents) {
            Slog.w(TAG, "Starting activity in task not in recents: " + inTask);
            mInTask = null;
        }

        mStartFlags = startFlags;
        // 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 & START_FLAG_ONLY_IF_NEEDED) != 0) {
            ActivityRecord checkedCaller = sourceRecord;
            if (checkedCaller == null) {
                checkedCaller = mSupervisor.mFocusedStack.topRunningNonDelayedActivityLocked(
                        mNotTop);
            }
            if (!checkedCaller.realActivity.equals(r.realActivity)) {
                // Caller is not the same as launcher, so always needed.
                mStartFlags &= ~START_FLAG_ONLY_IF_NEEDED;
            }
        }

        mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0;
    }

不难看到,在这个方法中,把整个参数赋值给ActivityStarter的全局变量,以供之后所有的流程使用。我们能看到这里能看到此时会稍微对一些Intent启动的flag进行处理。这里稍微展开一些值得注意的细节聊聊。

  • 1.getPreferedDisplayId(mSourceRecord, mStartActivity, options); 该方法通过启动的ActivityRecord来判断是否是VR模式。是则直接返回一个主displayId,否则从AMS获取DisplayId。这个DisplayId简单的说就是一个索引,可以通过它从SurfaceFlinger的Binder远程对象,从而找到一个逻辑显示器。

  • 2.mSupervisor.getLaunchParamsController().calculate()该方法实际上获取注册在LaunchParamsController中LaunchParamsModifier进行计算其表现在屏幕上的区域。实际上最基础有两个LaunchParamsModifier

  • 1.TaskLaunchParamsModifier

  • 2.ActivityLaunchParamsModifier

我们查看源码可以看到真正控制窗口大小变化(修改TaskRecord的Rect值,位置)的是TaskLaunchParamsModifier。而ActivityLaunchParamsModifier将当前的区域大小记录到LaunchParam中。我们平常可能很少这么使用,实际上在ActivityOptions的setLaunchBounds中能够控制新建的Activity的窗体大小和位置。

因此我们能够猜测文档经常所说的任务(也就是TaskRecord)是不是指Activity的窗体在AMS中的对象

  • 3.adjustLaunchFlagsToDocumentMode
    在Activity启动的flag中有一个FLAG_ACTIVITY_NEW_DOCUMENT。 这个flag如下:

给启动的Activity开一个新的任务记录,当使用new_document或者android: documentLaunchMode的时候,相同的实例会在最近任务表中产生不同的记录。
直接从new_document回退,直接回退桌面,想要改变这个行为,添加FLAG

可能有点抽象,看看这个gif。


new_doucment.gif

从这里的现象能够看到实际上new_document新建了一个新的历史记录以及一个新的栈。我们看看adjustLaunchFlagsToDocumentMode这个方法是怎么初步处理new_document的。

private int adjustLaunchFlagsToDocumentMode(ActivityRecord r, boolean launchSingleInstance,
            boolean launchSingleTask, int launchFlags) {
        if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
                (launchSingleInstance || launchSingleTask)) {
            launchFlags &=
                    ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK);
        } else {
            switch (r.info.documentLaunchMode) {
                case ActivityInfo.DOCUMENT_LAUNCH_NONE:
                    break;
                case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
                    break;
                case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
                    break;
                case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
                    launchFlags &= ~FLAG_ACTIVITY_MULTIPLE_TASK;
                    break;
            }
        }
        return launchFlags;
    }

这里面做了两个处理:

1.当存在new_document的时候,但是launchMode是singleTask或者singleInstanceTask的时候,将会关闭new_document以及MULTIPLE_TASK。因此你会发现此时使用了singleTask或者singleInstanceTask,将不会打开新的历史记录以及Activity栈。

2.当判断到在AndroidManifest中设置了documentLaunchMode,则为其添加flag到启动项中。
可以看到的是:

  • 如果读取到的是NONE不添加flag
  • 读取到INTO_EXISTING或者LAUNCH_ALWAYS 添加FLAG_ACTIVITY_NEW_DOCUMENT
  • 读取到NEVER,则关闭FLAG_ACTIVITY_MULTIPLE_TASK

如下面的情况:


singleTask/Instance的doucment.gif
    1. mLaunchTaskBehind 这个标志位判断是否新建的Activity盖在原来的Activity栈上。因此在原来的基础上判断了不是singleTask或者singleInstance,同时能打开new_document.
  • 5.如果打开Acitivty的mLaunchTaskBehind标志为true且是new_document没有指向下一个Activity数据,则默认添加的FLAG_ACTIVITY_NEW_TASK。

  • 6.接着如果发现FLAG_ACTIVITY_NEW_TASK打开了,设置了DOCUMENT_LAUNCH_ALWAYS或者mLaunchTaskBehind标志为true,则继续加上FLAG_ACTIVITY_MULTIPLE_TASK。

小节

注意,在非singleTask和singleInstance下使用new_document,刚好经过5和6步骤,因此出现了第一幅gif图的情况,打开了一个新的栈。

这里又出现了两个新的FLAG_ACTIVITY_MULTIPLE_TASK和FLAG_ACTIVITY_NEW_TASK。

这两个标志位一般是在创建新的任务栈才会使用。

FLAG_ACTIVITY_NEW_TASK:

新活动会成为历史栈中的新任务(一组活动)的开始。
如果新活动已存在于一个为它运行的任务中,那么不会启动,只会把该任务移到屏幕最前。
如果需要有返回flag则不能这个flag。

因此这个flag经常用在桌面开发里面。

FLAG_ACTIVITY_MULTIPLE_TASK:

用于创建一个新任务,并启动一个活动放进去
一般这个标志位会和FLAG_ACTIVITY_NEW_TASK或者FLAG_ACTIVITY_NEW_DOCUMENT一起使用。

    1. FLAG_ACTIVITY_NO_USER_ACTION判断这个标志位,从而设定mUserLeaving。FLAG_ACTIVITY_NO_USER_ACTION一般是用来阻止顶部Activity的onUserLeaveHint回调,在它被新启动的活动造成paused状态时.
  • 8.如果在启动的时候设置了TaskId,则通过id找到任务,判断ActivityRecord的状态来设置mDoResume和mAvoidMoveToFront。

-9. 当Activity已经被启动了设置了START_FLAG_ONLY_IF_NEEDED,则找到当前正在使用的Activity栈。调用topRunningNonDelayedActivityLocked运行当前顶部的Activity。

ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
            final TaskRecord task = mTaskHistory.get(taskNdx);
            final ArrayList<ActivityRecord> activities = task.mActivities;
            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                ActivityRecord r = activities.get(activityNdx);
                if (!r.finishing && !r.delayedResume && r != notTop && r.okToShowLocked()) {
                    return r;
                }
            }
        }
        return null;
    }

这里出现了另外一个数据对象TaskRecord和TaskHistroy。这辆对象象征着Activity的任务对象以及任务历史。

computeLaunchingTaskFlags 计算Task的flag

private void computeLaunchingTaskFlags() {
        ///存在要打开TaskRecord
        if (mSourceRecord == null && mInTask != null && mInTask.getStack() != null) {
            final Intent baseIntent = mInTask.getBaseIntent();
            final ActivityRecord root = mInTask.getRootActivity();
            if (baseIntent == null) {
                ActivityOptions.abort(mOptions);
                throw new IllegalArgumentException("Launching into task without base intent: "
                        + mInTask);
            }

       ///当启动的的模式是singleTask或者singleInstance,必须保证是根部
            if (isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
                if (!baseIntent.getComponent().equals(mStartActivity.intent.getComponent())) {
                    ActivityOptions.abort(mOptions);
                    throw new IllegalArgumentException("Trying to launch singleInstance/Task "
                            + mStartActivity + " into different task " + mInTask);
                }
                if (root != null) {
                    ActivityOptions.abort(mOptions);
                    throw new IllegalArgumentException("Caller with mInTask " + mInTask
                            + " has root " + root + " but target is singleInstance/Task");
                }
            }

         //根不为空时候设置为新建一个新的任务栈
            if (root == null) {
                final int flagsOfInterest = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK
                        | FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS;
                mLaunchFlags = (mLaunchFlags & ~flagsOfInterest)
                        | (baseIntent.getFlags() & flagsOfInterest);
                mIntent.setFlags(mLaunchFlags);
                mInTask.setIntent(mStartActivity);
                mAddingToTask = true;

            } else if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
                mAddingToTask = false;

            } else {
                mAddingToTask = true;
            }
//设置复用的TaskRecord为当前栈
            mReuseTask = mInTask;
        } else {
            mInTask = null;
         //当根部不为空的时候,则不会去启动当前这个任务
            if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null
                    && mSourceRecord.inFreeformWindowingMode())  {
                mAddingToTask = true;
            }
        }

        //不存在要打开的TaskRecord
        if (mInTask == null) {
            if (mSourceRecord == null) {
                // This activity is not being started from another...  in this
                // case we -always- start a new task.
                if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {
                    Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
                            "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
                    mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
                }
            } else if (mSourceRecord.launchMode == 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.
                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
            } else if (isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
                // The activity being started is a single instance...  it always
                // gets launched into its own task.
                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
            }
        }
    }

在这里分为两种情况:

  • 1.一种是本身知道TaskRecord,要启动的任务是哪个
    当要启动的目标TaskRecord,singleTask/singleInstance的启动模式必须要知道根部。否则的话,如果根部Activity为空则启动一个新的任务栈,把当前的任务栈作为复用对象。根部不为空,则把mInTask设置空,当作新建任务。
  • 2.一种是不知道将要启动TaskRecord是哪个,往往用于新建。
    此时会判断调用方的ActivityRecord为空,或者调用方本身是一个singleInstance,或者启动模式为singleTask或者singleInstance则重新启动一个新的任务作为开始。

这里有个函数稍微注意下TaskRecord.getRootActivity:
文件:/frameworks/base/services/core/java/com/android/server/am/TaskRecord.java

    /** Returns the first non-finishing activity from the root. */
    ActivityRecord getRootActivity() {
        for (int i = 0; i < mActivities.size(); i++) {
            final ActivityRecord r = mActivities.get(i);
            if (r.finishing) {
                continue;
            }
            return r;
        }
        return null;
    }

我们实际上能看到一个TaskRecord存储着一个mActivities的ActivityRecord集合。换句话说这个TaskRecord就是象征我上面说的任务。因此这个函数就是找到第一个没有被finish的Activity

computeSourceStack 获取调用方的Activity栈

    private void computeSourceStack() {
        if (mSourceRecord == null) {
            mSourceStack = null;
            return;
        }
        if (!mSourceRecord.finishing) {
            mSourceStack = mSourceRecord.getStack();
            return;
        }

        if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0) {
...
            mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
            mNewTaskInfo = mSourceRecord.info;

            // It is not guaranteed that the source record will have a task associated with it. For,
            // example, if this method is being called for processing a pending activity launch, it
            // is possible that the activity has been removed from the task after the launch was
            // enqueued.
            final TaskRecord sourceTask = mSourceRecord.getTask();
            mNewTaskIntent = sourceTask != null ? sourceTask.intent : null;
        }
        mSourceRecord = null;
        mSourceStack = null;
    }

此时我们能够看到,是从ActivityRecord拿到ActivityStack对象。如果调用方没有被finish则返回当前的ActivityStack。被finish了则添加一个NEW_TASK启动一个新的任务。

getReusableIntentActivity获取能够复用的Activity

看到这个名字就知道是专门处理singleTask,singleTop这些栈内唯一的启动模式

/**
     * Decide whether the new activity should be inserted into an existing task. Returns null
     * if not or an ActivityRecord with the task into which the new activity should be added.
     */
    private ActivityRecord getReusableIntentActivity() {
//根据启动模式是否能够放进已经存在的Task
        boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
                (mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
                || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK);
      
        putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
        ActivityRecord intentActivity = null;
        if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
            final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
            intentActivity = task != null ? task.getTopActivity() : null;
        } else if (putIntoExistingTask) {
            if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {

               intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
                       mStartActivity.isActivityTypeHome());
            } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
           
                intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
                        !(LAUNCH_SINGLE_TASK == mLaunchMode));
            } else {

                intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId);
            }
        }
        return intentActivity;
    }

如果上一段代码是为了找可以复用任务,而这段代码则是去寻找是否有可以复用的ActivityRecord。

这里逻辑如下:
putIntoExistingTask是一个是否能放入已经存早的任务的标志位。其判断的依据是FLAG_ACTIVITY_NEW_TASK打开了,但是要关闭FLAG_ACTIVITY_MULTIPLE_TASK;或者singleTask/singleInstance的启动模式(因为只有这两种启动模式才会去任务的栈内寻找复用的Activity)。

接着还要保证没有目标任务以及调用方本身没有要指向下一个ActivityRecord。

首先判断到有复用的任务(TaskRecord),则直接取出当前的任务的栈顶作为复用。

如果putIntoExistingTask为true分情况讨论

  • 1.当启动模式为singleInstance的时候,调用findActivityLocked查找是否存在可以复用的ActivityRecord,参数为mStartActivity.isActivityTypeHome()
  • 2.当打开了FLAG_ACTIVITY_LAUNCH_ADJACENT的时候,还是调用findActivityLocked,参数为是否为singleTask的boolean判断值
  • 3.否则则直接findTaskLocked,传入当前的主要逻辑显示器id。

那么核心就是这个findActivityLocked和findTaskLocked方法。稍微追踪一下。
文件:/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
            boolean compareIntentFilters) {
        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = display.getChildAt(stackNdx);
                final ActivityRecord ar = stack.findActivityLocked(
                        intent, info, compareIntentFilters);
                if (ar != null) {
                    return ar;
                }
            }
        }
        return null;
    }

这个方法和之前的isAnyStackLock相似,也是从mActivityDisplays获取ActivityDisplay,接着不断的从ActivityStack循环寻找和当前ActivityInfo相匹配的ActivityRecord。
文件:/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
                                      boolean compareIntentFilters) {
        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) {
            final TaskRecord task = mTaskHistory.get(taskNdx);
            final ArrayList<ActivityRecord> activities = task.mActivities;

            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                ActivityRecord r = activities.get(activityNdx);
                if (!r.okToShowLocked()) {
                    continue;
                }
                if (!r.finishing && r.userId == userId) {
                    if (compareIntentFilters) {
                        if (r.intent.filterEquals(intent)) {
                            return r;
                        }
                    } else {
                        if (r.intent.getComponent().equals(cls)) {
                            return r;
                        }
                    }
                }
            }
        }
        return null;
    }

我们可以发现,在ActivityStack实际存着一个TaskRecord的集合mTaskHistory。从名字我们可以猜测这是TaskRecord的历史栈,究竟有什么TaskRecord存在过ActivityStack。这样,我们再次从TaskRecord获取ActivityRecord列表,来查找是否存在一个可以用来复用的Activity。

这样我们就能理清楚一个包含关系:
ActivityDisplay- -> ActivityStack -> TaskRecord -> ActivityRecord

findTaskLocked

这段则是去寻找匹配的TaskRecord。

ActivityRecord findTaskLocked(ActivityRecord r, int displayId) {
        mTmpFindTaskResult.r = null;
        mTmpFindTaskResult.matchedByRootAffinity = false;
        ActivityRecord affinityMatch = null;
    
        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = display.getChildAt(stackNdx);
                if (!r.hasCompatibleActivityType(stack)) {

                    continue;
                }
                stack.findTaskLocked(r, mTmpFindTaskResult);
            
                if (mTmpFindTaskResult.r != null) {
                    if (!mTmpFindTaskResult.matchedByRootAffinity) {
                        return mTmpFindTaskResult.r;
                    } else if (mTmpFindTaskResult.r.getDisplayId() == displayId) {
                        // Note: since the traversing through the stacks is top down, the floating
                        // tasks should always have lower priority than any affinity-matching tasks
                        // in the fullscreen stacks
                        affinityMatch = mTmpFindTaskResult.r;
                    } else if (DEBUG_TASKS && mTmpFindTaskResult.matchedByRootAffinity) {
...
                    }
                }
            }
        }


        return affinityMatch;
    }

根据我们上面的包含关系是从TaskRecord来查找ActivityRecord,但是这里是通过ActivityRecord反向查找匹配的TaskRecord。因此逻辑复杂点。

因为包含关系还是依旧,所以会通过ActivityDisplay来找到对应的ActivityStack

void findTaskLocked(ActivityRecord target, FindTaskResult result) {
        Intent intent = target.intent;
        ActivityInfo info = target.info;
        ComponentName cls = intent.getComponent();
        if (info.targetActivity != null) {
            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;

        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
            final TaskRecord task = mTaskHistory.get(taskNdx);
            if (task.voiceSession != null) {
              
                continue;
            }
            if (task.userId != userId) {
                // Looking for a different task.

                continue;
            }

            // Overlays should not be considered as the task's logical top activity.
            final ActivityRecord r = task.getTopActivity(false /* includeOverlays */);
            if (r == null || r.finishing || r.userId != userId ||
                    r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
                continue;
            }
            if (!r.hasCompatibleActivityType(target)) {
                continue;
            }

            final Intent taskIntent = task.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 (taskIntent != null && taskIntent.getComponent() != null &&
                    taskIntent.getComponent().compareTo(cls) == 0 &&
                    Objects.equals(documentData, taskDocumentData)) {
                result.r = r;
                result.matchedByRootAffinity = false;
                break;
            } else if (affinityIntent != null && affinityIntent.getComponent() != null &&
                    affinityIntent.getComponent().compareTo(cls) == 0 &&
                    Objects.equals(documentData, taskDocumentData)) {
                result.r = r;
                result.matchedByRootAffinity = false;
                break;
            } else if (!isDocument && !taskIsDocument
                    && result.r == null && task.rootAffinity != null) {
                if (task.rootAffinity.equals(target.taskAffinity)) {
                    result.r = r;
                    result.matchedByRootAffinity = true;
                }
            } else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
        }
    }

因为一个TaskRecord会包含大量的ActivityRecord,这里并不是真的去循环匹配,而是循环ActivityStack中mTaskHistory,去拿到每个Taskrecord的顶部ActivityRecord去匹配。

当我们发现顶部运行的ActivityRecord是singleInstance启动模式,则跳过这个TaskRecord。去找下一个没有结束且是相同的userId的TaskRecord。

这里稍微注意一下,因为设定了new_document和taskAffinity会Activity的任务造成影响,因此需要分情况处理。

  • 1.当没设置taskAffinity,则取出TaskRecord的taskIntent,如果类名匹配,还有在intent中设置的Data数据相符则取出。
  • 2.当设置taskAffinity,则取出TaskRecord的affinityIntent,如果类名匹配,还有在intent中设置的Data数据相符则取出。
  • 3.当taskIntent / affinityIntent为空或者task没有打开new_document标志位,就会取出默认的taskAffinity去匹配名字,相符合则取出。
情景代入
 if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {

               intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
                       mStartActivity.isActivityTypeHome());
            } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
           
                intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
                        !(LAUNCH_SINGLE_TASK == mLaunchMode));
            } else {

                intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId);
            }

这里的mStartActivity就是在上面init步骤的时候,把从上一篇文章初始化好的activityRecord设置进来。

换算到当前代码情景,当putIntoExistingTask为true寻找复用ActivityRecord大致上可以为如下几个步骤:

  • 1.当此时是singleInstance,则需要判断是不是home。因为home也一般都是类似singleInstance模式。换句说,是home的时候将会除了查找包名一致之外,还会继续匹配intent里面的意图筛选。不是home的时候则是找到包名就返回。

  • 2.当打开了FLAG_ACTIVITY_LAUNCH_ADJACENT标志位,这个标志位一般是在分屏时候使用,新活动会显示在旧活动旁边。此时我们会发现,此时因为要分屏,那个singleTask会造成影响。所以在筛选的ActivityRecord时候,如果不是singleTask则不需要经过意图筛选直接通过类名返回,否则则通过意图筛选再返回ActivityRecord。

  • 3.当以上两者都不是,那么只是普通的singleTask模式或者singleInstance模式,为了减少时间复杂度,直接通过displayId去查找对应的Task的顶部正在运行的ActivityRecord。找到并且匹配taskAffinity则返回。

当复用的reusActivity不为空

 if (reusedActivity != null) {
...
            final boolean clearTopAndResetStandardLaunchMode =
                    (mLaunchFlags & (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED))
                            == (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
                    && mLaunchMode == LAUNCH_MULTIPLE;

            if (mStartActivity.getTask() == null && !clearTopAndResetStandardLaunchMode) {
                mStartActivity.setTask(reusedActivity.getTask());
            }

            if (reusedActivity.getTask().intent == null) {
                reusedActivity.getTask().setIntent(mStartActivity);
            }

            if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                    || isDocumentLaunchesIntoExisting(mLaunchFlags)
                    || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
                final TaskRecord task = reusedActivity.getTask();
                final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
                        mLaunchFlags);

                if (reusedActivity.getTask() == null) {
                    reusedActivity.setTask(task);
                }

                if (top != null) {
                    if (top.frontOfTask) {
                        top.getTask().setIntent(mStartActivity);
                    }
                    deliverNewIntent(top);
                }
            }

            mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, reusedActivity);

            reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);

            final ActivityRecord outResult =
                    outActivity != null && outActivity.length > 0 ? outActivity[0] : null;
            if (outResult != null && (outResult.finishing || outResult.noDisplay)) {
                outActivity[0] = reusedActivity;
            }

            if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
                resumeTargetStackIfNeeded();
                return START_RETURN_INTENT_TO_CALLER;
            }

            if (reusedActivity != null) {
                setTaskFromIntentActivity(reusedActivity);

                if (!mAddingToTask && mReuseTask == null) {
                    resumeTargetStackIfNeeded();
                    if (outActivity != null && outActivity.length > 0) {
                        outActivity[0] = reusedActivity;
                    }

                    return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;
                }
            }
        }

        if (mStartActivity.packageName == null) {
....
            return START_CLASS_NOT_FOUND;
        }

这里拆分3个步骤说明:

  • 1.如果当前的启动模式是standard(对应LAUNCH_MULTIPLE),并且打开了FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_RESET_TASK_IF_NEEDED,则说明要清空当前栈当前ActivityRecord一直到栈顶的数据,但是此时暂时不处理,仅仅设置了clearTopAndResetStandardLaunchMode一个boolean值。当这个boolean是false的时候,说明不用清掉Task上面的信息,因此,能够直接设置原来的TaskRecord进去

  • 2.当打开了FLAG_ACTIVITY_CLEAR_TOP标志位,或者打开了document标志位,或者打开了singleTask / singleInstance说明此时需要清掉TaskRecord的数据。最后调用deliverNewIntent。其中核心函数是performClearTaskForReuseLocked方法。这个方法就是清掉我们常说的singleTask顶部Activity,并且让当前Activity置顶。

文件:/frameworks/base/services/core/java/com/android/server/am/TaskRecord.java

    ActivityRecord performClearTaskForReuseLocked(ActivityRecord newR, int launchFlags) {
        mReuseTask = true;
        final ActivityRecord result = performClearTaskLocked(newR, launchFlags);
        mReuseTask = false;
        return result;
    }

  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 (mStack != null && mStack.finishActivityLocked(
                            r, Activity.RESULT_CANCELED, null, "clear-task-stack", false)) {
                        --activityNdx;
                        --numActivities;
                    }
                }


                if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
                        && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0
                        && !ActivityStarter.isDocumentLaunchesIntoExisting(launchFlags)) {
                    if (!ret.finishing) {
                        if (mStack != null) {
                            mStack.finishActivityLocked(
                                    ret, Activity.RESULT_CANCELED, null, "clear-task-top", false);
                        }
                        return null;
                    }
                }

                return ret;
            }
        }

        return null;
    }

实际上,这个mActivities这个ArrayList。存放着当前任务内所有的Activity。此时当作一个栈的话,就是从尾部开始往头部循环查找。那么这个算法分为两步:

  • 从mActivities尾部往头部查找到被复用的Activity
  • 找到被复用的Activity在往尾部依次循环,并且调用ActivityStack的finish的方法结束这个Activity,并且减少引用。

但是这里有个特殊处理,如果是standard启动的话,并且FLAG_ACTIVITY_SINGLE_TOP打开了。此时也会结束当前的Activity。那么此时调用不了deliverNewIntent,也就说这种方式实现类似singleTop的效果是不会会调onNewIntent。而是会当作重新启动。

  • 3.经过上面的步骤,不管是否已经清除栈顶的数据。接下来都会,确定已经是要加入到对应的筛选出来栈中。

因此需要开始加入到栈中,但是根据我上面列出来的在AMS中的包含关系,我们先要找到ActivityDisplay之后,再找到其中TaskRecord,最后再把ActivityRecord加入其中。

必定是这个逻辑,那么在这个情况复用的ActivityRecord找到了,为了保证其正确性,就要对TaskRecord做重新处理,把当前的TaskRecord放到最顶部,究竟哪里算是顶部,接下来看看核心方法之一setTargetStackAndMoveToFrontIfNeeded。

setTargetStackAndMoveToFrontIfNeeded
private ActivityRecord setTargetStackAndMoveToFrontIfNeeded(ActivityRecord intentActivity) {
        mTargetStack = intentActivity.getStack();
        mTargetStack.mLastPausedActivity = null;
//获取顶部信息
        final ActivityStack focusStack = mSupervisor.getFocusedStack();
        ActivityRecord curTop = (focusStack == null)
                ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);

        final TaskRecord topTask = curTop != null ? curTop.getTask() : null;
//顶部复用栈的信息合法
        if (topTask != null
                && (topTask != intentActivity.getTask() || topTask != focusStack.topTask())
                && !mAvoidMoveToFront) {
            mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
            if (mSourceRecord == null || (mSourceStack.getTopActivity() != null &&
                    mSourceStack.getTopActivity().getTask() == mSourceRecord.getTask())) {
                
                if (mLaunchTaskBehind && mSourceRecord != null) {
                    intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
                }

               
                final boolean willClearTask =
                        (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                            == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
                if (!willClearTask) {
                    final ActivityStack launchStack = getLaunchStack(
                            mStartActivity, mLaunchFlags, mStartActivity.getTask(), mOptions);
                    final TaskRecord intentTask = intentActivity.getTask();
                    if (launchStack == null || launchStack == mTargetStack) {
                      
                        mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions,
                                mStartActivity.appTimeTracker, "bringingFoundTaskToFront");
                        mMovedToFront = true;
                    } else if (launchStack.inSplitScreenWindowingMode()) {
                        if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {

                            intentTask.reparent(launchStack, ON_TOP,
                                    REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
                                    "launchToSide");
                        } else {
                            
                            mTargetStack.moveTaskToFrontLocked(intentTask,
                                    mNoAnimation, mOptions, mStartActivity.appTimeTracker,
                                    "bringToFrontInsteadOfAdjacentLaunch");
                        }
                        mMovedToFront = launchStack != launchStack.getDisplay()
                                .getTopStackInWindowingMode(launchStack.getWindowingMode());
                    } else if (launchStack.mDisplayId != mTargetStack.mDisplayId) {

                        intentActivity.getTask().reparent(launchStack, ON_TOP,
                                REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
                                "reparentToDisplay");
                        mMovedToFront = true;
                    } else if (launchStack.isActivityTypeHome()
                            && !mTargetStack.isActivityTypeHome()) {

                        intentActivity.getTask().reparent(launchStack, ON_TOP,
                                REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
                                "reparentingHome");
                        mMovedToFront = true;
                    }
                    mOptions = null;

                    intentActivity.showStartingWindow(null /* prev */, false /* newTask */,
                            true /* taskSwitch */);
                }
            }
        }

        mTargetStack = intentActivity.getStack();
        if (!mMovedToFront && mDoResume) {
       
            mTargetStack.moveToFront("intentActivityFound");
        }

        mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(),
                WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, mTargetStack);


        if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
            return mTargetStack.resetTaskIfNeededLocked(intentActivity, mStartActivity);
        }
        return intentActivity;
    }
  • 1.首先找到当前AMS的焦点ActivityStack,也就是正在和用户交互的ActivityStack找到mTaskHistory的顶部正在运行的ActivityRecord。同时找到对应的TaskRecord。

  • 2.当发现顶部的TaskRecord不为空,同时顶部的Task和要服用的Task不是同一个时候,并且此时mAvoidMoveToFront为false的时候(mAvoidMoveToFront是在init的步骤初始化好的,这个参数是由ActivityOptions设置的,一般是false)。当判断从此时的intent的启动flag,没有打开FLAG_ACTIVITY_NEW_TASK以及FLAG_ACTIVITY_CLEAR_TASK,说明不用清空顶部的栈内所有的信息信息,会分为几种情况,把当前的Task移动栈顶。将会在下面的场景回归继续分析。

  • 3.不管有没有清空,最后都需要把当前的栈ActivityStack移动到最前方,注意这里的最前端是指和人交互最直接,最顶层的位置。

  • 4.接下来判断FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标志位,这个标志一个的时候,没办法做到什么,一般几个标志位一起联动,来判断当前是否需要重置当前的mHistoryTask或者新建一个栈。

大致分为这四步骤,接下来让我们看看整个流程是值得注意的步骤怎么处理的。

剖析setTargetStackAndMoveToFrontIfNeeded

先看看第二步骤中值得注意的方法getLaunchStack,获取当前即将要启动的栈。


    private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, TaskRecord task,
            ActivityOptions aOptions) {
        if (mReuseTask != null) {
            return mReuseTask.getStack();
        }

        if (((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0)
                 || mPreferredDisplayId != DEFAULT_DISPLAY) {
            // We don't pass in the default display id into the get launch stack call so it can do a
            // full resolution.
            final int candidateDisplay =
                    mPreferredDisplayId != DEFAULT_DISPLAY ? mPreferredDisplayId : INVALID_DISPLAY;
            return mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP, candidateDisplay);
        }

        final ActivityStack parentStack = task != null ? task.getStack(): mSupervisor.mFocusedStack;

        if (parentStack != mSupervisor.mFocusedStack) {
            // If task's parent stack is not focused - use it during adjacent launch.
            return parentStack;
        } else {
            if (mSupervisor.mFocusedStack != null && task == mSupervisor.mFocusedStack.topTask()) {
                return mSupervisor.mFocusedStack;
            }

            if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) {

                final int activityType = mSupervisor.resolveActivityType(r, mOptions, task);
                return parentStack.getDisplay().getOrCreateStack(
                        WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, activityType, ON_TOP);
            } else {

                final ActivityStack dockedStack =
                        mSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
                if (dockedStack != null && !dockedStack.shouldBeVisible(r)) {

                    return mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP);
                } else {
                    return dockedStack;
                }
            }
        }
    }

如果当前要复用的Task不为空的时候,直接复用。但是实际上Activity存在分屏操作等特殊情况,因此可能需要特殊处理。

所以分为以下3个步骤:

  • 1.先判断当前有没有打开FLAG_ACTIVITY_LAUNCH_ADJACENT标志位,并且当前的显示器id和当前的主屏id不一致。此时将会判断mPreferredDisplayId是否是默认的主屏幕id,不是则取当前屏幕id,不然则是无效id。接着调用mSupervisor.getLaunchStack进一步的确认真正的ActivityStack。

  • 2.不然实际上是打开了FLAG_ACTIVITY_LAUNCH_ADJACENT标志位,如果加上NEW_TASK就可能会启动到分屏。

则获取当前即将启动的ActivityRecord对应的task,如果为空则获取把当前的Task设置为当前焦点TaskRecord。当前的焦点ActivityStack和当前父亲ActivityStack(为当前taskrecord或者当前焦点的taskrecord,取决于当前的taskrecord是否为空)是不是同一个说明可以直接到分屏,就直接返回ActivityStack。

如果是同一个当前的TaskRecord和焦点ActivityStack的顶部历史Task是同一个,说明不用直接到分屏,而是直接返回ActivityStack。

如果当前的TaskRecord和当前焦点的ActivityStack的mHistoryTask的顶部不是同一个,且当前父亲ActivityStack不为空,且打开了inSplitScreenPrimaryWindowingMode(分屏模式)则说明此时想当前的task想要显示到顶部,却没办法,此时需要从ActivityStack中根据情况获取或者新建ActivityStack。

如果为空,则获取桌面的ActivityStack。

因此这一段的意思实际上就是根据分屏情况以及FLAG_ACTIVITY_LAUNCH_ADJACENT获取合理的ActivityStack。

其中值得注意的有mSupervisor.getLaunchStack这个函数,从ActivityStackSupervisor中进一步获取ActivityStack。

ActivityStackSupervisor 创建与获取ActivityStack

文件:/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

<T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
            @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop,
            int candidateDisplayId) {
        int taskId = INVALID_TASK_ID;
        int displayId = INVALID_DISPLAY;
       
        if (options != null) {
            taskId = options.getLaunchTaskId();
            displayId = options.getLaunchDisplayId();

        }


        if (taskId != INVALID_TASK_ID) {
            options.setLaunchTaskId(INVALID_TASK_ID);
            final TaskRecord task = anyTaskForIdLocked(taskId,
                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options, onTop);
            options.setLaunchTaskId(taskId);
            if (task != null) {
                return task.getStack();
            }
        }

        final int activityType = resolveActivityType(r, options, candidateTask);
        T stack = null;

        if (displayId == INVALID_DISPLAY) {
            displayId = candidateDisplayId;
        }
        if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
            if (r != null) {
                stack = (T) getValidLaunchStackOnDisplay(displayId, r);
                if (stack != null) {
                    return stack;
                }
            }
            final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId);
            if (display != null) {
                stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
                if (stack != null) {
                    return stack;
                }
            }
        }


        stack = null;
        ActivityDisplay display = null;
        if (candidateTask != null) {
            stack = candidateTask.getStack();
        }
        if (stack == null && r != null) {
            stack = r.getStack();
        }
        if (stack != null) {
            display = stack.getDisplay();
            if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
                final int windowingMode =
                        display.resolveWindowingMode(r, options, candidateTask, activityType);
                if (stack.isCompatible(windowingMode, activityType)) {
                    return stack;
                }
                if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
                        && display.getSplitScreenPrimaryStack() == stack
                        && candidateTask == stack.topTask()) {

                    return stack;
                }
            }
        }

        if (display == null
                || !canLaunchOnDisplay(r, display.mDisplayId)
                // TODO: Can be removed once we figure-out how non-standard types should launch
                // outside the default display.
                || (activityType != ACTIVITY_TYPE_STANDARD
                && activityType != ACTIVITY_TYPE_UNDEFINED)) {
            display = getDefaultDisplay();
        }

        return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
    }

这里又可以分为以下几个步骤:

  • 1.如果ActivityOption中配置了taskId,则通过taskId从ActivityStack的mHistory中获取对应的TaskRecord,并且返回TaskRecord对应的ActivityStack。

  • 2.如果没有taskId,则只能从当前的displayId去查找id。如果是ActivityOptions中是无效的displayId则从获取从方法中传下来的displayId获取。

此时在验证一次displayId,如果有效,且当前的ActivityRecord是可以被启动在显示器上,则通过getValidLaunchStackOnDisplay方法从ActivityDisplay只能够获取与当前模式兼容的ActivityStack,不为空则返回。

如果当前的ActivityRecord为空,说明此时情况特殊,没有任何合适的ActivityStack则调用getActivityDisplayOrCreateLocked或者创建获取对应的ActivityDisplay,最后通过getOrCreateStack创建获取ActivityStack。

  • 3.当传下来的taskId和displayId都是非法的时候,就没有办法从这两个线索去搜索合适的ActivityStack。说明此时可能是分屏情况,一个有Activity,一个完全没启动。

AMS接下来会按照方式,先从当前的TaskRecord中获取到对应的栈,如果Activity的启动和Window当前的模式兼容则直接返回。如果不兼容,说明此时可能处于分屏,判断到当前的分屏的Task是当前task,且是历史栈中的顶部,则获取分屏的信息。

  • 4.最后,这样还没办法处理,说明此时处于一切的初始状态,调用getOrCreateStack创建ActivityStack。

那么,我们追溯到了ActivityStack这个重要的数据结构的创建了。先来看看怎么通过getActivityDisplayOrCreateLocked创建获取ActivityStack。

getActivityDisplayOrCreateLocked 获取或者创建ActivityDisplay
  ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) {
        ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
        if (activityDisplay != null) {
            return activityDisplay;
        }
        if (mDisplayManager == null) {

            return null;
        }
        final Display display = mDisplayManager.getDisplay(displayId);
        if (display == null) {
            return null;
        }

        activityDisplay = new ActivityDisplay(this, display);
        attachDisplay(activityDisplay);
        calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
        mWindowManager.onDisplayAdded(displayId);
        return activityDisplay;
    }

从这里我们就能看到在创建或者获取ActivityStack中涉及一个核心的服务DisplayManager。该服务是用来管理显示,具体点就是管理各种显示器的。这一块就会放到WMS中的解析。现在只需要明白,根据id获取逻辑显示器。

首先从mActivityDisplays缓存查找对应的id的ActivityDisplay,找不到则通过DisplayManagerService去找对应id的逻辑显示器。还找不到,说明根本没有在DisplayManagerService中注册这种显示器,直接返回空。找到逻辑显示器,说明此AMS刚开始启动或者这种显示器第一次使用,因此需要新建一个ActivityDisplay。

因此创建ActivityDisplay分为四步骤:

  • 1.新建一个对象
  • 2.通过attachDisplay把新建ActivityDisplay添加到mActivityDisplays
  • 3.通过default_minimal_size_resizable_task设置全局mDefaultMinSizeOfResizeableTask大小,这个大小象征着Activity如果没有指定大小,就指定这个默认大小。
  • 4.把当前的displayId绑定到WindowManagerService中。

别忘了,此时我们目的是要获取ActivityDisplay中的ActivityStack。因此我们接下来看看getOrCreateStack,又是如何创建处理ActivityStack的。

getOrCreateStack 从ActivityDisplay获取ActivityStack
    <T extends ActivityStack> T getOrCreateStack(int windowingMode, int activityType,
            boolean onTop) {
        if (!alwaysCreateStack(windowingMode, activityType)) {
            T stack = getStack(windowingMode, activityType);
            if (stack != null) {
                return stack;
            }
        }
        return createStack(windowingMode, activityType, onTop);
    }

能看到的是这里有个alwaysCreateStack,判断当前是否总是需要创建ActivityStack。当判断为否则尝试着从缓存中获取合适的。如果找不到则创建一个ActivityStack。

private boolean alwaysCreateStack(int windowingMode, int activityType) {
        return activityType == ACTIVITY_TYPE_STANDARD
                && (windowingMode == WINDOWING_MODE_FULLSCREEN
                || windowingMode == WINDOWING_MODE_FREEFORM
                || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
    }

当Activity的启动方式是standard启动模式,并且window的模式是全屏,或者分屏第二个屏幕,或者是自由窗口模式(在7.0之后,Android系统支持类似PC操作系统的窗口模式),则判断需要总要创建新的ActivityStack。

ActivityDisplay获取ActivityStack getStack
<T extends ActivityStack> T getStack(int windowingMode, int activityType) {
        if (activityType == ACTIVITY_TYPE_HOME) {
            return (T) mHomeStack;
        } else if (activityType == ACTIVITY_TYPE_RECENTS) {
            return (T) mRecentsStack;
        }
        if (windowingMode == WINDOWING_MODE_PINNED) {
            return (T) mPinnedStack;
        } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
            return (T) mSplitScreenPrimaryStack;
        }

        for (int i = mStacks.size() - 1; i >= 0; --i) {
            final ActivityStack stack = mStacks.get(i);
            if (stack.isCompatible(windowingMode, activityType)) {
                return (T) stack;
            }
        }
        return null;
    }

我们能够看到,ActivityStack在9.0中比起7.0复杂了很多。这里面根据windowMode以及activityType,ActivityDisplay把ActivityStack区分为如下几种ActivityStack:

  • 1.mHomeStack 象征着桌面的Activity栈
  • 2.mRecentsStack 象征着应用中最近使用的ActivityStack
  • 3.mPinnedStack
  • 4.mSplitScreenPrimaryStack 分屏后主屏幕的ActivityStack
  • 5.没有指定,直接循环获取,和当前的windowMode以及activityType相符的stack返回(当activityType不是普通的模式或者未声明的时候,只需要匹配activityType,否则则匹配windowMode)。
ActivityDisplay创建ActivityStack
<T extends ActivityStack> T createStack(int windowingMode, int activityType, boolean onTop) {

        ....

        final int stackId = getNextStackId();
        return createStackUnchecked(windowingMode, activityType, stackId, onTop);
    }

可以看到,ActivityStack的id设置是依次增加的。

@VisibleForTesting
    <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
            int stackId, boolean onTop) {
        if (windowingMode == WINDOWING_MODE_PINNED) {
            return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop);
        }
        return (T) new ActivityStack(
                        this, stackId, mSupervisor, windowingMode, activityType, onTop);
    }

可以看到实际上ActivityStack分为两种类型一种是普通的ActivityStack,一种是PinnedActivityStack。而PinnedActivityStack是一种固定显示的栈,其默认的activityType是standard,windowMode是WINDOWING_MODE_PINNED。

setTargetStackAndMoveToFrontIfNeeded 场景回归

回到当前的场景,获得了即将要登陆的ActivityStack之后,setTargetStackAndMoveToFrontIfNeeded,会做第二步极其重要的事情,就是移动当前的ActivityStack到最顶部,也就是人机交互的栈顶。我们重新看看源码,

  if (!willClearTask) {
                    final ActivityStack launchStack = getLaunchStack(
                            mStartActivity, mLaunchFlags, mStartActivity.getTask(), mOptions);
                    final TaskRecord intentTask = intentActivity.getTask();
                    if (launchStack == null || launchStack == mTargetStack) {
                      
                        mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions,
                                mStartActivity.appTimeTracker, "bringingFoundTaskToFront");
                        mMovedToFront = true;
                    } else if (launchStack.inSplitScreenWindowingMode()) {
                        if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {

                            intentTask.reparent(launchStack, ON_TOP,
                                    REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
                                    "launchToSide");
                        } else {
                            
                            mTargetStack.moveTaskToFrontLocked(intentTask,
                                    mNoAnimation, mOptions, mStartActivity.appTimeTracker,
                                    "bringToFrontInsteadOfAdjacentLaunch");
                        }
                        mMovedToFront = launchStack != launchStack.getDisplay()
                                .getTopStackInWindowingMode(launchStack.getWindowingMode());
                    } else if (launchStack.mDisplayId != mTargetStack.mDisplayId) {

                        intentActivity.getTask().reparent(launchStack, ON_TOP,
                                REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
                                "reparentToDisplay");
                        mMovedToFront = true;
                    } else if (launchStack.isActivityTypeHome()
                            && !mTargetStack.isActivityTypeHome()) {

                        intentActivity.getTask().reparent(launchStack, ON_TOP,
                                REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
                                "reparentingHome");
                        mMovedToFront = true;
                    }
                    mOptions = null;

                    intentActivity.showStartingWindow(null /* prev */, false /* newTask */,
                            true /* taskSwitch */);
                }
            }
        }

当确定不需要清空Activity栈内的信息时候,将会获取登陆ActivityStack。这个ActivityStack的获取是如果显式的设置了复用目标,则使用,不然则是获取在init中初始好的启动的调用方ActivityStack,当然如果遇到分屏,虚拟屏等特殊情况另说。

此时将会依据即将登陆的ActivityStack和当前ActivityRecord对应的ActivityStack做比较。可以分为以下几种情况:

  • 1.当要启动的栈与目标一致,或者要启动的栈为空。
  • 2.要启动的栈的windowMode为分屏模式
  • 3.要启动的栈displayId和当前的ActivityRecord不一致
  • 4.要启动的栈是Home,而当前的ActivityRecord不是。

在情况2-3都会通过reparent,把Task迁移到launchStack中。

稍微看看情况一

情况一当要启动的栈与目标一致,或者要启动的栈为空

此时正是我们正常的启动流程。也就是说会唤起moveTaskToFrontLocked方法。把当前的栈移动到用户交互的栈顶。

final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, ActivityOptions options,
            AppTimeTracker timeTracker, String reason) {
...

        final ActivityStack topStack = getDisplay().getTopStack();
        final ActivityRecord topActivity = topStack != null ? topStack.getTopActivity() : null;
        final int numTasks = mTaskHistory.size();
        final int index = mTaskHistory.indexOf(tr);
...

        try {
            getDisplay().deferUpdateImeTarget();

            insertTaskAtTop(tr, null);
            final ActivityRecord top = tr.getTopActivity();
            if (top == null || !top.okToShowLocked()) {
                if (top != null) {
                    mStackSupervisor.mRecentTasks.add(top.getTask());
                }
                ActivityOptions.abort(options);
                return;
            }

    
            final ActivityRecord r = topRunningActivityLocked();
            mStackSupervisor.moveFocusableActivityStackToFrontLocked(r, reason);
 ....
            mStackSupervisor.resumeFocusedStackTopActivityLocked();
...
        } finally {
...
        }
    }

我们可以看到这个核心的算法实际上很简单,实际上就是首先先从mHistoryTasks找到对应的TaskRecord。如果找不到则立即返回,找到了则调用insertTaskAtTop,把这个TaskRecord插入到mHistoryTask顶部,如果此时的顶部ActivityRecord暂时不能显示,把TaskRecord添加到ActivityStackSupervisor的mRecentTasks,最近使用的Activity的任务列表中,这样就相当于调用过。

接着调用moveFocusableActivityStackToFrontLocked,处理ActivityStack这个方法最后会调用ActivityStack的moveToFront。

moveToFront方法做的事情有两件,第一件把ActivityStackSupervisor的FocusStack设置为当前的ActivityStack,第二件事,把当前这个ActivityStack设置ActivityDisplay中的mStacks (存放着ActivityStack的ArrayList)的前端。

最后再调用resumeFocusedStackTopActivityLocked启动Activity的Resume
这样就完成了当发现有复用ActivityRecord时候,TaskRecord和ActivityStack的移动到交互栈顶。

场景回归startActivityUnchecked的复用Activity情况

在setTargetStackAndMoveToFrontIfNeeded中什么时候需要移动TaskRecord以及ActivityStack呢?主要还是当前顶部的ActivityRecord对应的TaskRecord既不是目标启动的栈,也不是当前的焦点的栈对应顶部TaskRecord。也就是说,可能需要移动把复用的TaskRecord进行一次Task之间的移动。

 private void setTaskFromIntentActivity(ActivityRecord intentActivity) {
        if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {.
            final TaskRecord task = intentActivity.getTask();
            task.performClearTaskLocked();
            mReuseTask = task;
            mReuseTask.setIntent(mStartActivity);
        } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
            ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity,
                    mLaunchFlags);
            if (top == null) {
                mAddingToTask = true;

                mSourceRecord = intentActivity;
                final TaskRecord task = mSourceRecord.getTask();
                if (task != null && task.getStack() == null) {
                    mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
                            mLaunchFlags, mOptions);
                    mTargetStack.addTask(task,
                            !mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
                }
            }
        } else if (mStartActivity.realActivity.equals(intentActivity.getTask().realActivity)) {
            if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                        || LAUNCH_SINGLE_TOP == mLaunchMode)
                    && intentActivity.realActivity.equals(mStartActivity.realActivity)) {
                if (intentActivity.frontOfTask) {
                    intentActivity.getTask().setIntent(mStartActivity);
                }
                deliverNewIntent(intentActivity);
            } else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) {
                mAddingToTask = true;
                mSourceRecord = intentActivity;
            }
        } else if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
            mAddingToTask = true;
            mSourceRecord = intentActivity;
        } else if (!intentActivity.getTask().rootWasReset) {
            intentActivity.getTask().setIntent(mStartActivity);
        }
    }

当不是上面的情况,需要 setTaskFromIntentActivity,进一步做处理。

  • 如果打开FLAG_ACTIVITY_NEW_TASK 以及FLAG_ACTIVITY_CLEAR_TASK,则设置mReuseTask为当前的Task。
  • 如果打开了FLAG_ACTIVITY_CLEAR_TOP的标志位,同时singleInstance或者singleTask的一种,则清空Task,拿到顶部的Task,最后重新添加到ActivityStack
  • 3.要启动的Activity和当前的Activity是同一个,同时打开了singleTop标志位,以及singleTop启动,直接调用onNewIntent.如果是相同过滤条件,则把启动SourceRecord设置为当前ActivityRecord。

最后再调用resumeTargetStackIfNeeded,resume当前复用的Activity。

startActivityUnchecked当没有复用Activity时候

在处理完FLAG_ACTIVITY_SINGLE_TOP这种情况之后,将会处理正常启动的Activity。

 int result = START_SUCCESS;
        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
                && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
            newTask = true;
            result = setTaskFromReuseOrCreateNewTask(taskToAffiliate, topStack);
        } else if (mSourceRecord != null) {
            result = setTaskFromSourceRecord();
        } else if (mInTask != null) {
            result = setTaskFromInTask();
        } else {
            setTaskToCurrentTopOrCreateNewTask();
        }
...
mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
                mOptions);
         mOptions);
        if (mDoResume) {
            final ActivityRecord topTaskActivity =
                    mStartActivity.getTask().topRunningActivityLocked();
            if (!mTargetStack.isFocusable()
                    || (topTaskActivity != null && topTaskActivity.mTaskOverlay
                    && mStartActivity != topTaskActivity)) {
           ....
            } else {
                if (mTargetStack.isFocusable() && !mSupervisor.isFocusedStack(mTargetStack)) {
                    mTargetStack.moveToFront("startActivityUnchecked");
                }
                mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
                        mOptions);
            }
        } else if (mStartActivity != null) {
...
        }

此时能看到为了找到,真正需要的ActivityStack启动Activity。因此这里分为三种情况:

  • 1.当启动的flag打开了FLAG_ACTIVITY_NEW_TASK,并且mAddingToTask为false。当前的Task根部没有任何的Activity这个标志位true,不是如果打开了
    new_task为false,否则为true。或者是使用了自由窗口模式,复用Task的时候为null也为true。因此这个标志位起作用一般是当一个Task 中为空,才会新建一个TaskRecord。

  • 2.当mSourceRecord不为空,把新的ActivityRecord绑定到启动者的TaskRecord上

  • 3.剩下的情况:启动时带了mInTask,ActivityRecord绑定到mInTask。都不是则直接找焦点的ActivityStack上栈顶的Task,直接绑定(几乎不可能发生)。

第一种情况打开了FLAG_ACTIVITY_NEW_TASK

当我们打开了FLAG_ACTIVITY_NEW_TASK,computeStackFocus获取目标的ActivityStack。这个方法已经在上面分析过了。

private int setTaskFromReuseOrCreateNewTask(
            TaskRecord taskToAffiliate, ActivityStack topStack) {
        mTargetStack = computeStackFocus(mStartActivity, true, mLaunchFlags, mOptions);
        if (mReuseTask == null) {
            final TaskRecord task = mTargetStack.createTaskRecord(
                    mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
                    mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
                    mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
                    mVoiceInteractor, !mLaunchTaskBehind /* toTop */, mStartActivity, mSourceRecord,
                    mOptions);
            addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
            updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds);

...
        } else {
            addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask");
        }
....
        if (mDoResume) {
            mTargetStack.moveToFront("reuseOrNewTask");
        }
        return START_SUCCESS;
    }

 private void addOrReparentStartingActivity(TaskRecord parent, String reason) {
        if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) {
            parent.addActivityToTop(mStartActivity);
        } else {
            mStartActivity.reparent(parent, parent.mActivities.size() /* top */, reason);
        }
    }

这里代入情景,如果是分屏,则判断是否打开FLAG_ACTIVITY_LAUNCH_ADJACENT。根据这个标志位不同到本ActivityStack还是对面。如果是普通的启动获取的当前要启动的ActivityRecord对应的ActivityStack。FLAG_ACTIVITY_NEW_TASK是否生成新的Task的依据是mReuseTask是否设置。而这个对象本质上是ActivityOptions设置的,一般的为空。

最后判断启动的Task为空或者和启动的Task一致,则调用TaskRecord的addActivityToTop把当前要启动的Activity放到TaskRecord的顶部,否则则调用ActivityRecord的reparent。

 void reparent(TaskRecord newTask, int position, String reason) {
        final TaskRecord prevTask = task;
        if (prevTask == newTask) {
            throw new IllegalArgumentException(reason + ": task=" + newTask
                    + " is already the parent of r=" + this);
        }
        if (prevTask != null && newTask != null && prevTask.getStack() != newTask.getStack()) {
            throw new IllegalArgumentException(reason + ": task=" + newTask
                    + " is in a different stack (" + newTask.getStackId() + ") than the parent of"
                    + " r=" + this + " (" + prevTask.getStackId() + ")");
        }
mWindowContainerController.reparent(newTask.getWindowContainerController(), position);
        final ActivityStack prevStack = prevTask.getStack();

        if (prevStack != newTask.getStack()) {
            prevStack.onActivityRemovedFromStack(this);
        }
        // Remove the activity from the old task and add it to the new task.
        prevTask.removeActivity(this, true /* reparenting */);

        newTask.addActivityAtIndex(position, this);
    }

能看到,这个方法是清空TaskRecord原来存在其中的ActivityRecord,并且添加到当前新的TaskRecord的顶部。以完成TaskRecord的切换。

当mSourceRecord不为空

 private int setTaskFromSourceRecord() {
        if (mService.getLockTaskController().isLockTaskModeViolation(mSourceRecord.getTask())) {
            Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
            return START_RETURN_LOCK_TASK_MODE_VIOLATION;
        }

        final TaskRecord sourceTask = mSourceRecord.getTask();
        final ActivityStack sourceStack = mSourceRecord.getStack();
        // We only want to allow changing stack in two cases:
        // 1. If the target task is not the top one. Otherwise we would move the launching task to
        //    the other side, rather than show two side by side.
        // 2. If activity is not allowed on target display.
        final int targetDisplayId = mTargetStack != null ? mTargetStack.mDisplayId
                : sourceStack.mDisplayId;
        final boolean moveStackAllowed = sourceStack.topTask() != sourceTask
                || !mStartActivity.canBeLaunchedOnDisplay(targetDisplayId);
        if (moveStackAllowed) {
            mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.getTask(),
                    mOptions);
            if (mTargetStack == null && targetDisplayId != sourceStack.mDisplayId) {
                mTargetStack = mService.mStackSupervisor.getValidLaunchStackOnDisplay(
                        sourceStack.mDisplayId, mStartActivity);
            }
            if (mTargetStack == null) {
                mTargetStack = mService.mStackSupervisor.getNextValidLaunchStackLocked(
                        mStartActivity, -1 /* currentFocus */);
            }
        }

        if (mTargetStack == null) {
            mTargetStack = sourceStack;
        } else if (mTargetStack != sourceStack) {
            sourceTask.reparent(mTargetStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
                    DEFER_RESUME, "launchToSide");
        }

        final TaskRecord topTask = mTargetStack.topTask();
        if (topTask != sourceTask && !mAvoidMoveToFront) {
            mTargetStack.moveTaskToFrontLocked(sourceTask, mNoAnimation, mOptions,
                    mStartActivity.appTimeTracker, "sourceTaskToFront");
        } else if (mDoResume) {
            mTargetStack.moveToFront("sourceStackToFront");
        }

        if (!mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0) {
            ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags);
            mKeepCurTransition = true;
            if (top != null) {
                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask());
                deliverNewIntent(top);
                mTargetStack.mLastPausedActivity = null;
                if (mDoResume) {
                    mSupervisor.resumeFocusedStackTopActivityLocked();
                }
                ActivityOptions.abort(mOptions);
                return START_DELIVERED_TO_TOP;
            }
        } else if (!mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
            final ActivityRecord top = sourceTask.findActivityInHistoryLocked(mStartActivity);
            if (top != null) {
                final TaskRecord task = top.getTask();
                task.moveActivityToFrontLocked(top);
                top.updateOptionsLocked(mOptions);
                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task);
                deliverNewIntent(top);
                mTargetStack.mLastPausedActivity = null;
                if (mDoResume) {
                    mSupervisor.resumeFocusedStackTopActivityLocked();
                }
                return START_DELIVERED_TO_TOP;
            }
        }

        addOrReparentStartingActivity(sourceTask, "setTaskFromSourceRecord");

        return START_SUCCESS;
    }

这个应该是最常见的情况,分步骤说明

  • 1.启动栈的顶部TaskRecord和启动方的TaskRecord不一致或者启动方的不允许显示。则需要移动到其他的ActivityStack。因此此时比较特殊,因此按照上面解析的逻辑,是从有效的displayId中查找与当前模式兼容的ActivityStack,找不到则创建一个ActivityDisplay,再创建对应的ActivityStack。

    1. mTargetStack为空,则是默认的情况。设置为当前的启动方的ActivityStack。发现目标ActivityStack和启动的ActivityStack不是一个,则需要把启动方TaskRecord,重绑到目标的ActivityStack。
  • 3.把目标ActivityStack移动到最前方。也就是设置为焦点ActivityStack,同时设置到ActivityDisplay集合的顶部

  • 4 mAddingToTask为false,打开了FLAG_ACTIVITY_CLEAR_TOP,则清除启动方的TaskRecord中的顶部。顶部不为空,则调用newIntent,需要resume,再调用resume。

  • 5.mAddingToTask为false,打开了FLAG_ACTIVITY_REORDER_TO_FRONT标志位。这个标志位是在setTargetStackAndMoveToFrontIfNeeded默认设置的。说明此时有复用,但是栈相同,此时会从TaskRecord中获取复用的ActivityRecord。最后调用onNewIntent。

  • 6.不过经历什么步骤,都会通过startActivityLocked要把TaskRecord添加到mHistoryTasks顶部。

最后调用ActivityStackSupervisor的resumeFocusedStackTopActivityLocked(因为方法此时传下来默认是true)。

至此就完成了ActivityStack以及TaskRecord如何转化到交互的栈顶流程。

小节

限于篇幅问题,本片总结将会之后一起放出。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容