【源码解析】Activity的启动模式

Activity的启动模式中我们用的最多的是Standard标准模式,其实Activity一共有四种启动模式。我们将从Android源码中介绍Activity的四种启动模式,并比较它们之间的区别。

一、Activity的启动模式

1.1 Standard:标准模式

Standard是标准模式,也就是系统默认的启动模式。每次启动一个Activity时都会创建一个实例,而不管该Activity是否存在。
在这种模式下,一个任务栈可以有多个实例,每个实例也可以属于不同的任务栈。
在这种模式下,谁启动了这个Activity,那么这个Activity就运行在启动它的Activity所在的任务栈中。
举个例子,Activity A启动了Activity B(Activity B是标准模式),那么B就会进入到A所在的栈中。
在平时的开发中,有时候我们会用ApplicationContext去启动一个标准模式的Activity,这个时候会报错,错误如下:

Calling startActivity from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASKS flag. Is this really what you want?

这是因为标准模式的Activity默认会进入启动它的Activity所属的任务栈中,但是由于非Activity类型的Context(如ApplicationContext)并没有所谓的任务栈,所以要启动的Activity找不到任务栈就有问题了。
解决的方法是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK 标记位,这样启动Activity的时候就会创建一个新的任务栈并将Activity入栈。

1.2 SingleTop:栈顶复用模式

SingleTop是一种栈顶复用模式。
栈顶复用意味着如果将要启动的Activity是在任务栈的栈顶,则该Activity不会重建,而且Activity的onNewIntent方法会被回调,通过该回调方法,我们可以取出当前请求的信息。
但是当要启动的Activity不在栈顶时,Activity仍然会重建。此时的行为和Standard模式一致。

接下来我们通过源码来验证上述的结论是否正确(下面的源码是基于Android SDK 22的基础进行分析的)。
从Activity的startActivity开始,startActivity最终调用了startActivityForResult方法:

public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
    if (mParent == null) {
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);
        ...
    } else {
        if (options != null) {
            mParent.startActivityFromChild(this, intent, requestCode, options);
        } else {
            // Note we want to go through this method for compatibility with
            // existing applications that may have overridden it.
            mParent.startActivityFromChild(this, intent, requestCode);
        }
    }
}

当mParent == null时,执行Instrumentation的execStartActivity。

Note:我们忽略mParent != null的情况,因为Activity有父类Parent已经被废弃。有兴趣可以研究TabActivity。
Instrumentation的execStartActivity方法如下:

public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    ...
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess();
        int result = ActivityManagerNative.getDefault()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}

最终会执行ActivityManagerService.startActivity方法,startActivity方法又执行了startActivityAsUser方法,然后转到了ActivityStackSupervisor的startActivityMayWait方法,startActivityMayWait方法又调用了startActivityLocked,startActivityLocked方法接着调用了startActivityUncheckedLocked,我们直接看startActivityUncheckedLocked方法:

final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
        boolean doResume, Bundle options, TaskRecord inTask) {
    ...省略
    
    //重点关注这三个本地变量
    //1、栈顶复用模式
    final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP;
    //单实例模式
    final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE;
    //栈内复用模式
    final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;

    ...省略

    ActivityRecord notTop =
            (launchFlags & Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;

    
    if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
        ActivityRecord checkedCaller = sourceRecord;
        if (checkedCaller == null) {
            checkedCaller = mFocusedStack.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;
        }
    }
    //2、该变量为true时,表示将要启动的Activity加入到任务栈
    boolean addingToTask = false;
    TaskRecord reuseTask = null;

    // If the caller is not coming from another activity, but has given us an
    // explicit task into which they would like us to launch the new activity,
    // then let's see about doing that.
    if (sourceRecord == null && inTask != null && inTask.stack != null) {
        final Intent baseIntent = inTask.getBaseIntent();
        final ActivityRecord root = inTask.getRootActivity();
        if (baseIntent == null) {
            ActivityOptions.abort(options);
            throw new IllegalArgumentException("Launching into task without base intent: "
                    + inTask);
        }

        // If this task is empty, then we are adding the first activity -- it
        // determines the root, and must be launching as a NEW_TASK.
        if (launchSingleInstance || launchSingleTask) {
            if (!baseIntent.getComponent().equals(r.intent.getComponent())) {
                ActivityOptions.abort(options);
                throw new IllegalArgumentException("Trying to launch singleInstance/Task "
                        + r + " into different task " + inTask);
            }
            if (root != null) {
                ActivityOptions.abort(options);
                throw new IllegalArgumentException("Caller with inTask " + inTask
                        + " has root " + root + " but target is singleInstance/Task");
            }
        }

        // If task is empty, then adopt the interesting intent launch flags in to the
        // activity being started.
        if (root == null) {
            final int flagsOfInterest = Intent.FLAG_ACTIVITY_NEW_TASK
                    | Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT
                    | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
            launchFlags = (launchFlags&~flagsOfInterest)
                    | (baseIntent.getFlags()&flagsOfInterest);
            intent.setFlags(launchFlags);
            inTask.setIntent(r);
            addingToTask = true;

        // If the task is not empty and the caller is asking to start it as the root
        // of a new task, then we don't actually want to start this on the task.  We
        // will bring the task to the front, and possibly give it a new intent.
        } else if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
            addingToTask = false;

        } else {
            addingToTask = true;
        }

        reuseTask = inTask;
    } else {
        inTask = null;
    }

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

    ActivityInfo newTaskInfo = null;
    Intent newTaskIntent = null;
    ActivityStack sourceStack;
    if (sourceRecord != null) {
        if (sourceRecord.finishing) {
            // If the source is finishing, we can't further count it as our source.  This
            // is because the task it is associated with may now be empty and on its way out,
            // so we don't want to blindly throw it in to that task.  Instead we will take
            // the NEW_TASK flow and try to find a task for it. But save the task information
            // so it can be used when creating the new task.
            if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
                Slog.w(TAG, "startActivity called from finishing " + sourceRecord
                        + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
                launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
                newTaskInfo = sourceRecord.info;
                newTaskIntent = sourceRecord.task.intent;
            }
            sourceRecord = null;
            sourceStack = null;
        } else {
            sourceStack = sourceRecord.task.stack;
        }
    } else {
        sourceStack = null;
    }

    boolean movedHome = false;
    ActivityStack targetStack;

    intent.setFlags(launchFlags);
    final boolean noAnimation = (launchFlags & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0;


    if (((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
            (launchFlags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
            || launchSingleInstance || launchSingleTask) {
        // If bring to front is requested, and no result is requested and we have not
        // been given an explicit task to launch in to, and
        // we can find a task that was started with this same
        // component, then instead of launching bring that one to the front.
        if (inTask == null && r.resultTo == null) {
            // See if there is a task to bring to the front.  If this is
            // a SINGLE_INSTANCE activity, there can be one and only one
            // instance of it in the history, and it is always in its own
            // unique task, so we do a special search.
            ActivityRecord intentActivity = !launchSingleInstance ?
                    findTaskLocked(r) : findActivityLocked(intent, r.info);
            if (intentActivity != null) {
                // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused
                // but still needs to be a lock task mode violation since the task gets
                // cleared out and the device would otherwise leave the locked task.
                if (isLockTaskModeViolation(intentActivity.task,
                        (launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                        == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
                    showLockTaskToast();
                    Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
                    return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
                }
                if (r.task == null) {
                    r.task = intentActivity.task;
                }
                if (intentActivity.task.intent == null) {
                    // This task was started because of movement of
                    // the activity based on affinity...  now that we
                    // are actually launching it, we can assign the
                    // base intent.
                    intentActivity.task.setIntent(r);
                }
                ...省略
                if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                        == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
                    // The caller has requested to completely replace any
                    // existing task with its new activity.  Well that should
                    // not be too hard...
                    reuseTask = intentActivity.task;
                    reuseTask.performClearTaskLocked();
                    reuseTask.setIntent(r);
                } else if ((launchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                        || launchSingleInstance || launchSingleTask) {
                    ...省略
                } else if (r.realActivity.equals(intentActivity.task.realActivity)) {
                
                    //3、如果启动模式是栈顶复用模式,而且要启动的Activity和任务栈顶的Activity的包名和路径名是一样的,
                    //则调用intentActivity.deliverNewIntentLocked方法,这里的intentActivity为ActivityRecord类型。
                    //注意:这里并没有将addingToTask变量置为true
                    //注意:这里intentActivity.realActivity和r.realActivity都是ComponentName类型,非Activity类型。
                    if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 || launchSingleTop)
                            && intentActivity.realActivity.equals(r.realActivity)) {
                        ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r,
                                intentActivity.task);
                        if (intentActivity.frontOfTask) {
                            intentActivity.task.setIntent(r);
                        }
                        intentActivity.deliverNewIntentLocked(callingUid, r.intent,
                                r.launchedFromPackage);
                    } else if (!r.intent.filterEquals(intentActivity.task.intent)) {
                        // In this case we are launching the root activity
                        // of the task, but with a different intent.  We
                        // should start a new instance on top.
                        addingToTask = true;
                        sourceRecord = intentActivity;
                    }
                } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
                    // In this case an activity is being launched in to an
                    // existing task, without resetting that task.  This
                    // is typically the situation of launching an activity
                    // from a notification or shortcut.  We want to place
                    // the new activity on top of the current task.
                    addingToTask = true;
                    sourceRecord = intentActivity;
                } else if (!intentActivity.task.rootWasReset) {
                    // In this case we are launching in to an existing task
                    // that has not yet been started from its front door.
                    // The current task has been brought to the front.
                    // Ideally, we'd probably like to place this new task
                    // at the bottom of its stack, but that's a little hard
                    // to do with the current organization of the code so
                    // for now we'll just drop it.
                    intentActivity.task.setIntent(r);
                }
                if (!addingToTask && reuseTask == null) {
                    // We didn't do anything...  but it was needed (a.k.a., client
                    // don't use that intent!)  And for paranoia, make
                    // sure we have correctly resumed the top activity.
                    if (doResume) {
                        targetStack.resumeTopActivityLocked(null, options);
                        if (!movedToFront) {
                            // Make sure to notify Keyguard as well if we are not running an app
                            // transition later.
                            notifyActivityDrawnForKeyguard();
                        }
                    } else {
                        ActivityOptions.abort(options);
                    }
                    return ActivityManager.START_TASK_TO_FRONT;
                }
            }
        }
    }

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

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

    boolean newTask = false;
    boolean keepCurTransition = false;

    TaskRecord taskToAffiliate = launchTaskBehind && sourceRecord != null ?
            sourceRecord.task : null;

    // Should this be considered a new task?
    if (r.resultTo == null && inTask == null && !addingToTask
            && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
        ...
    } else if (sourceRecord != null) {
        final TaskRecord sourceTask = sourceRecord.task;
        if (isLockTaskModeViolation(sourceTask)) {
            Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
            return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
        }
        targetStack = sourceTask.stack;
        targetStack.moveToFront("sourceStackToFront");
        final TaskRecord topTask = targetStack.topTask();
        if (topTask != sourceTask) {
            targetStack.moveTaskToFrontLocked(sourceTask, noAnimation, options,
                    r.appTimeTracker, "sourceTaskToFront");
        }
        if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
            ...
        } else if (!addingToTask &&
                (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
            ....

    } else if (inTask != null) {
        // The caller is asking that the new activity be started in an explicit
        // task it has provided to us.
        if (isLockTaskModeViolation(inTask)) {
            Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
            return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
        }
        targetStack = inTask.stack;
        targetStack.moveTaskToFrontLocked(inTask, noAnimation, options, r.appTimeTracker,
                "inTaskToFront");

        // Check whether we should actually launch the new activity in to the task,
        // or just reuse the current activity on top.
        ActivityRecord top = inTask.getTopActivity();
        if (top != null && top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
            if ((launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
                    || launchSingleTop || launchSingleTask) {
                ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
                if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
                    // We don't need to start a new activity, and
                    // the client said not to do anything if that
                    // is the case, so this is it!
                    return ActivityManager.START_RETURN_INTENT_TO_CALLER;
                }
                top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
                return ActivityManager.START_DELIVERED_TO_TOP;
            }
        }
        //4、addingToTask为false,直接返回。
        if (!addingToTask) {
            // We don't actually want to have this activity added to the task, so just
            // stop here but still tell the caller that we consumed the intent.
            ActivityOptions.abort(options);
            return ActivityManager.START_TASK_TO_FRONT;
        }

        r.setTask(inTask, null);
        if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r
                + " in explicit task " + r.task);

    } else {
        ...省略
    }

    mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
            intent, r.getUriPermissionsLocked(), r.userId);

    if (sourceRecord != null && sourceRecord.isRecentsActivity()) {
        r.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
    }
    if (newTask) {
        EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
    }
    ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
    targetStack.mLastPausedActivity = null;
    targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
    if (!launchTaskBehind) {
        // Don't set focus on an activity that's going to the back.
        mService.setFocusedActivityLocked(r, "startedActivity");
    }
    return ActivityManager.START_SUCCESS;
}

代码有点长,我们只看关键部分,已经在代码中使用注释说明:
1、判断当前Activity的启动模式是否是“栈顶复用模式”。
2、定义addingToTask变量,当该变量为true时,表示将要启动的Activity加入到任务栈(即启动一个新的Activity)。
3、在栈顶复用模式下,而且启动的Activity和任务栈顶的Activity的包名和路径名相同,调用ActivityRecord的deliverNewIntentLocked方法,该方法会最后调用Activity的onNewIntent方法。这一步骤并没有改变addingToTask变量的值。
4、addingToTask变量为false,直接返回。因此不会执行后面启动一个新的Activity的逻辑。

上面四个步骤论证了当启动的Activity为SingleTop模式时,如果要启动的Activity已经处于任务栈顶,则不会启动新的Activity。

我们继续接着上面的第三步,看一下ActivityRecord的deliverNewIntentLocked方法是否会最终调用Activity的onNewIntent方法。

final void deliverNewIntentLocked(int callingUid, Intent intent, String referrer) {
    // The activity now gets access to the data associated with this Intent.
    service.grantUriPermissionFromIntentLocked(callingUid, packageName,
            intent, getUriPermissionsLocked(), userId);
    // We want to immediately deliver the intent to the activity if
    // it is currently the top resumed activity...  however, if the
    // device is sleeping, then all activities are stopped, so in that
    // case we will deliver it if this is the current top activity on its
    // stack.
    final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
    boolean unsent = true;
    if ((state == ActivityState.RESUMED
            || (service.isSleeping() && task.stack != null
                && task.stack.topRunningActivityLocked(null) == this))
            && app != null && app.thread != null) {
        try {
            ArrayList ar = new ArrayList<>(1);
            ar.add(rintent);
                        //调用了ActivityThread的scheduleNewIntent方法
            app.thread.scheduleNewIntent(ar, appToken);
            unsent = false;
        } catch (RemoteException e) {
            Slog.w(TAG, "Exception thrown sending new intent to " + this, e);
        } catch (NullPointerException e) {
            Slog.w(TAG, "Exception thrown sending new intent to " + this, e);
        }
    }
    if (unsent) {
        addNewIntentLocked(rintent);
    }
}

deliverNewIntentLocked方法又调用了ActivityThread的scheduleNewIntent方法,scheduleNewIntent方法经过内部跳转,执行了sendMessage方法,并通过Handler发出一个消息,该消息的处理执行了handleNewIntent方法,handleNewIntent方法经过内部跳转,最终执行了deliverNewIntents方法。

private void deliverNewIntents(ActivityClientRecord r, List intents) {
    final int N = intents.size();
    for (int i=0; i

在deliverNewIntents方法中,调用了Instrumentation的callActivityOnNewIntent方法。

public void callActivityOnNewIntent(Activity activity, Intent intent) {
    activity.onNewIntent(intent);
}

最终该方法执行了Activity的onNewIntent方法。

至此,我们才算论证了上述的结论的另外一半,即SingleTop模式下启动的Activity,如果该Activity在任务栈顶,则Activity不会创建,而且该Activity的onNewIntent方法还会被回调执行。
同时,我们也看到当要启动的Activity不在栈顶时(即上述注释3中的intentActivity.realActivity.equals(r.realActivity)为false),这样行为就和Standard模式一致了。

1.2 SingleInstance:栈内复用模式

Activity的启动流程和上面SingleTop模式一样,这里着重分析ActivityStackSupervisor的startActivityUncheckedLocked方法。

final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
        boolean doResume, Bundle options, TaskRecord inTask) {
    final Intent intent = r.intent;
    final int callingUid = r.launchedFromUid;

    // 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);
        inTask = null;
    }

    final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP;
    final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE;
    //1、栈内复用模式
    final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;

    int launchFlags = intent.getFlags();
    if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
            (launchSingleInstance || launchSingleTask)) {
        // We have a conflict between the Intent and the Activity manifest, manifest wins.
        Slog.i(TAG, "Ignoring FLAG_ACTIVITY_NEW_DOCUMENT, launchMode is " +
                "\"singleInstance\" or \"singleTask\"");
        launchFlags &=
                ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.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 &= ~Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
                break;
        }
    }

    final boolean launchTaskBehind = r.mLaunchTaskBehind
            && !launchSingleTask && !launchSingleInstance
            && (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0;

    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.");
        r.resultTo.task.stack.sendActivityResultLocked(-1,
                r.resultTo, r.resultWho, r.requestCode,
                Activity.RESULT_CANCELED, null);
        r.resultTo = null;
    }

    if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
        launchFlags |= Intent.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 ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
        if (launchTaskBehind
                || r.info.documentLaunchMode == ActivityInfo.DOCUMENT_LAUNCH_ALWAYS) {
            launchFlags |= Intent.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.
    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 = getFocusedStack().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;
        }
    }

    boolean addingToTask = false;
    TaskRecord reuseTask = null;

    // If the caller is not coming from another activity, but has given us an
    // explicit task into which they would like us to launch the new activity,
    // then let's see about doing that.
    if (sourceRecord == null && inTask != null && inTask.stack != null) {
        final Intent baseIntent = inTask.getBaseIntent();
        final ActivityRecord root = inTask.getRootActivity();
        if (baseIntent == null) {
            ActivityOptions.abort(options);
            throw new IllegalArgumentException("Launching into task without base intent: "
                    + inTask);
        }

        // If this task is empty, then we are adding the first activity -- it
        // determines the root, and must be launching as a NEW_TASK.
        if (launchSingleInstance || launchSingleTask) {
            if (!baseIntent.getComponent().equals(r.intent.getComponent())) {
                ActivityOptions.abort(options);
                throw new IllegalArgumentException("Trying to launch singleInstance/Task "
                        + r + " into different task " + inTask);
            }
            if (root != null) {
                ActivityOptions.abort(options);
                throw new IllegalArgumentException("Caller with inTask " + inTask
                        + " has root " + root + " but target is singleInstance/Task");
            }
        }

        // If task is empty, then adopt the interesting intent launch flags in to the
        // activity being started.
        if (root == null) {
            final int flagsOfInterest = Intent.FLAG_ACTIVITY_NEW_TASK
                    | Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT
                    | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
            launchFlags = (launchFlags&~flagsOfInterest)
                    | (baseIntent.getFlags()&flagsOfInterest);
            intent.setFlags(launchFlags);
            inTask.setIntent(r);
            addingToTask = true;

        // If the task is not empty and the caller is asking to start it as the root
        // of a new task, then we don't actually want to start this on the task.  We
        // will bring the task to the front, and possibly give it a new intent.
        } else if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
            addingToTask = false;

        } else {
            addingToTask = true;
        }

        reuseTask = inTask;
    } else {
        inTask = null;
    }

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

    ActivityInfo newTaskInfo = null;
    Intent newTaskIntent = null;
    ActivityStack sourceStack;
    if (sourceRecord != null) {
        if (sourceRecord.finishing) {
            // If the source is finishing, we can't further count it as our source.  This
            // is because the task it is associated with may now be empty and on its way out,
            // so we don't want to blindly throw it in to that task.  Instead we will take
            // the NEW_TASK flow and try to find a task for it. But save the task information
            // so it can be used when creating the new task.
            if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
                Slog.w(TAG, "startActivity called from finishing " + sourceRecord
                        + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
                launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
                newTaskInfo = sourceRecord.info;
                newTaskIntent = sourceRecord.task.intent;
            }
            sourceRecord = null;
            sourceStack = null;
        } else {
            sourceStack = sourceRecord.task.stack;
        }
    } else {
        sourceStack = null;
    }

    boolean movedHome = false;
    ActivityStack targetStack;

    intent.setFlags(launchFlags);

    // We may want to try to place the new activity in to an existing task.  We always
    // do this if the target activity is singleTask or singleInstance; we will also do
    // this if NEW_TASK has been requested, and there is not an additional qualifier telling
    // us to still place it in a new task: multi task, always doc mode, or being asked to
    // launch this as a new task behind the current one.
    if (((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
            (launchFlags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
            || launchSingleInstance || launchSingleTask) {
        // If bring to front is requested, and no result is requested and we have not
        // been given an explicit task to launch in to, and
        // we can find a task that was started with this same
        // component, then instead of launching bring that one to the front.
        if (inTask == null && r.resultTo == null) {
            // See if there is a task to bring to the front.  If this is
            // a SINGLE_INSTANCE activity, there can be one and only one
            // instance of it in the history, and it is always in its own
            // unique task, so we do a special search.
            ActivityRecord intentActivity = !launchSingleInstance ?
                    findTaskLocked(r) : findActivityLocked(intent, r.info);
            //2、如果存在Activity所需要的任务栈
            if (intentActivity != null) {
                if (isLockTaskModeViolation(intentActivity.task)) {
                    showLockTaskToast();
                    Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
                    return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
                }
                if (r.task == null) {
                    r.task = intentActivity.task;
                }
                targetStack = intentActivity.task.stack;
                targetStack.mLastPausedActivity = null;
                if (DEBUG_TASKS) Slog.d(TAG, "Bring to front target: " + targetStack
                        + " from " + intentActivity);
                targetStack.moveToFront("intentActivityFound");
                if (intentActivity.task.intent == null) {
                    // This task was started because of movement of
                    // the activity based on affinity...  now that we
                    // are actually launching it, we can assign the
                    // base intent.
                    intentActivity.task.setIntent(r);
                }
                // If the target task is not in the front, then we need
                // to bring it to the front...  except...  well, with
                // SINGLE_TASK_LAUNCH it's not entirely clear.  We'd like
                // to have the same behavior as if a new instance was
                // being started, which means not bringing it to the front
                // if the caller is not itself in the front.
                final ActivityStack lastStack = getLastStack();
                ActivityRecord curTop = lastStack == null?
                        null : lastStack.topRunningNonDelayedActivityLocked(notTop);
                boolean movedToFront = false;
                if (curTop != null && (curTop.task != intentActivity.task ||
                        curTop.task != lastStack.topTask())) {
                    r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
                    if (sourceRecord == null || (sourceStack.topActivity() != null &&
                            sourceStack.topActivity().task == sourceRecord.task)) {
                        // We really do want to push this one into the
                        // user's face, right now.
                        if (launchTaskBehind && sourceRecord != null) {
                            intentActivity.setTaskToAffiliateWith(sourceRecord.task);
                        }
                        movedHome = true;
                        targetStack.moveTaskToFrontLocked(intentActivity.task, r, options,
                                "bringingFoundTaskToFront");
                        if ((launchFlags &
                                (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
                                == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
                            // Caller wants to appear on home activity.
                            intentActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
                        }
                        options = null;
                        movedToFront = true;
                    }
                }
                // If the caller has requested that the target task be
                // reset, then do so.
                if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
                    intentActivity = targetStack.resetTaskIfNeededLocked(intentActivity, r);
                }
                if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
                    // We don't need to start a new activity, and
                    // the client said not to do anything if that
                    // is the case, so this is it!  And for paranoia, make
                    // sure we have correctly resumed the top activity.
                    if (doResume) {
                        resumeTopActivitiesLocked(targetStack, null, options);

                        // Make sure to notify Keyguard as well if we are not running an app
                        // transition later.
                        if (!movedToFront) {
                            notifyActivityDrawnForKeyguard();
                        }
                    } else {
                        ActivityOptions.abort(options);
                    }
                    return ActivityManager.START_RETURN_INTENT_TO_CALLER;
                }
                if ((launchFlags &
                        (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK))
                        == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK)) {
                    // The caller has requested to completely replace any
                    // existing task with its new activity.  Well that should
                    // not be too hard...
                    reuseTask = intentActivity.task;
                    reuseTask.performClearTaskLocked();
                    reuseTask.setIntent(r);
                } else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
                        || launchSingleInstance || launchSingleTask) {
                    // In this situation we want to remove all activities
                    // from the task up to the one being started.  In most
                    // cases this means we are resetting the task to its
                    // initial state.
                    //3、如果启动模式是栈内复用模式,那么我们将移除要启动的Activity之上所有的Activity,并调用要启动
                    //的Activity的onNewIntent方法。
                    //如果Activity找不到,则新建一个Activity。这里如果top为null,则表示需要新建一个Activity。
                    //因此,这里将addingToTask标记为true。
                    ActivityRecord top =
                            intentActivity.task.performClearTaskLocked(r, launchFlags);
                    if (top != null) {
                        if (top.frontOfTask) {
                            // Activity aliases may mean we use different
                            // intents for the top activity, so make sure
                            // the task now has the identity of the new
                            // intent.
                            top.task.setIntent(r);
                        }
                        ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT,
                                r, top.task);
                        top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
                    } else { 
                        // A special case: we need to
                        // start the activity because it is not currently
                        // running, and the caller has asked to clear the
                        // current task to have this activity at the top.
                        addingToTask = true;
                        // Now pretend like this activity is being started
                        // by the top of its task, so it is put in the
                        // right place.
                        sourceRecord = intentActivity;
                    }
                } else if (r.realActivity.equals(intentActivity.task.realActivity)) {
                    // In this case the top activity on the task is the
                    // same as the one being launched, so we take that
                    // as a request to bring the task to the foreground.
                    // If the top activity in the task is the root
                    // activity, deliver this new intent to it if it
                    // desires.
                    if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 || launchSingleTop)
                            && intentActivity.realActivity.equals(r.realActivity)) {
                        ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r,
                                intentActivity.task);
                        if (intentActivity.frontOfTask) {
                            intentActivity.task.setIntent(r);
                        }
                        intentActivity.deliverNewIntentLocked(callingUid, r.intent,
                                r.launchedFromPackage);
                    } else if (!r.intent.filterEquals(intentActivity.task.intent)) {
                        // In this case we are launching the root activity
                        // of the task, but with a different intent.  We
                        // should start a new instance on top.
                        addingToTask = true;
                        sourceRecord = intentActivity;
                    }
                } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
                    // In this case an activity is being launched in to an
                    // existing task, without resetting that task.  This
                    // is typically the situation of launching an activity
                    // from a notification or shortcut.  We want to place
                    // the new activity on top of the current task.
                    addingToTask = true;
                    sourceRecord = intentActivity;
                } else if (!intentActivity.task.rootWasReset) {
                    // In this case we are launching in to an existing task
                    // that has not yet been started from its front door.
                    // The current task has been brought to the front.
                    // Ideally, we'd probably like to place this new task
                    // at the bottom of its stack, but that's a little hard
                    // to do with the current organization of the code so
                    // for now we'll just drop it.
                    intentActivity.task.setIntent(r);
                }
                if (!addingToTask && reuseTask == null) {
                    // We didn't do anything...  but it was needed (a.k.a., client
                    // don't use that intent!)  And for paranoia, make
                    // sure we have correctly resumed the top activity.
                    if (doResume) {
                        targetStack.resumeTopActivityLocked(null, options);
                        if (!movedToFront) {
                            // Make sure to notify Keyguard as well if we are not running an app
                            // transition later.
                            notifyActivityDrawnForKeyguard();
                        }
                    } else {
                        ActivityOptions.abort(options);
                    }
                    return ActivityManager.START_TASK_TO_FRONT;
                }
            }
        }
    }

    //String uri = r.intent.toURI();
    //Intent intent2 = new Intent(uri);
    //Slog.i(TAG, "Given intent: " + r.intent);
    //Slog.i(TAG, "URI is: " + uri);
    //Slog.i(TAG, "To intent: " + intent2);

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

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

    boolean newTask = false;
    boolean keepCurTransition = false;

    TaskRecord taskToAffiliate = launchTaskBehind && sourceRecord != null ?
            sourceRecord.task : null;

    // Should this be considered a new task?
    //4、如果找不到启动的Activity想要的任务栈,则重新创建一个任务栈并创建Activity的实例并将Activity放入到栈中。
    if (r.resultTo == null && inTask == null && !addingToTask
            && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
        if (isLockTaskModeViolation(reuseTask)) {
            Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
            return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
        }
        newTask = true;
        targetStack = adjustStackFocus(r, newTask);
        if (!launchTaskBehind) {
            targetStack.moveToFront("startingNewTask");
        }
        if (reuseTask == null) {
            r.setTask(targetStack.createTaskRecord(getNextTaskId(),
                    newTaskInfo != null ? newTaskInfo : r.info,
                    newTaskIntent != null ? newTaskIntent : intent,
                    voiceSession, voiceInteractor, !launchTaskBehind /* toTop */),
                    taskToAffiliate);
            if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " +
                    r.task);
        } else {
            r.setTask(reuseTask, taskToAffiliate);
        }
        if (!movedHome) {
            if ((launchFlags &
                    (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME))
                    == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) {
                // Caller wants to appear on home activity, so before starting
                // their own activity we will bring home to the front.
                r.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
            }
        }
    } else if (sourceRecord != null) {
        final TaskRecord sourceTask = sourceRecord.task;
        if (isLockTaskModeViolation(sourceTask)) {
            Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
            return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
        }
        targetStack = sourceTask.stack;
        targetStack.moveToFront("sourceStackToFront");
        final TaskRecord topTask = targetStack.topTask();
        if (topTask != sourceTask) {
            targetStack.moveTaskToFrontLocked(sourceTask, r, options, "sourceTaskToFront");
        }
        if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
            // In this case, we are adding the activity to an existing
            // task, but the caller has asked to clear that task if the
            // activity is already running.
            ActivityRecord top = sourceTask.performClearTaskLocked(r, launchFlags);
            keepCurTransition = true;
            if (top != null) {
                ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
                top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
                // For paranoia, make sure we have correctly
                // resumed the top activity.
                targetStack.mLastPausedActivity = null;
                if (doResume) {
                    targetStack.resumeTopActivityLocked(null);
                }
                ActivityOptions.abort(options);
                return ActivityManager.START_DELIVERED_TO_TOP;
            }
        } else if (!addingToTask &&
                (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
            // In this case, we are launching an activity in our own task
            // that may already be running somewhere in the history, and
            // we want to shuffle it to the front of the stack if so.
            final ActivityRecord top = sourceTask.findActivityInHistoryLocked(r);
            if (top != null) {
                final TaskRecord task = top.task;
                task.moveActivityToFrontLocked(top);
                ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, task);
                top.updateOptionsLocked(options);
                top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
                targetStack.mLastPausedActivity = null;
                if (doResume) {
                    targetStack.resumeTopActivityLocked(null);
                }
                return ActivityManager.START_DELIVERED_TO_TOP;
            }
        }
        // An existing activity is starting this new activity, so we want
        // to keep the new one in the same task as the one that is starting
        // it.
        r.setTask(sourceTask, null);
        if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                + " in existing task " + r.task + " from source " + sourceRecord);

    } else if (inTask != null) {
        // The calling is asking that the new activity be started in an explicit
        // task it has provided to us.
        if (isLockTaskModeViolation(inTask)) {
            Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
            return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
        }
        targetStack = inTask.stack;
        targetStack.moveTaskToFrontLocked(inTask, r, options, "inTaskToFront");

        // Check whether we should actually launch the new activity in to the task,
        // or just reuse the current activity on top.
        ActivityRecord top = inTask.getTopActivity();
        if (top != null && top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
            if ((launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
                    || launchSingleTop || launchSingleTask) {
                ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
                if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
                    // We don't need to start a new activity, and
                    // the client said not to do anything if that
                    // is the case, so this is it!
                    return ActivityManager.START_RETURN_INTENT_TO_CALLER;
                }
                top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
                return ActivityManager.START_DELIVERED_TO_TOP;
            }
        }

        if (!addingToTask) {
            // We don't actually want to have this activity added to the task, so just
            // stop here but still tell the caller that we consumed the intent.
            ActivityOptions.abort(options);
            return ActivityManager.START_TASK_TO_FRONT;
        }

        r.setTask(inTask, null);
        if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                + " in explicit 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.
        targetStack = adjustStackFocus(r, newTask);
        targetStack.moveToFront("addingToTopTask");
        ActivityRecord prev = targetStack.topActivity();
        r.setTask(prev != null ? prev.task : targetStack.createTaskRecord(getNextTaskId(),
                        r.info, intent, null, null, true), null);
        mWindowManager.moveTaskToTop(r.task.taskId);
        if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                + " in new guessed " + r.task);
    }

    mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
            intent, r.getUriPermissionsLocked(), r.userId);

    if (sourceRecord != null && sourceRecord.isRecentsActivity()) {
        r.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
    }
    if (newTask) {
        EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
    }
    ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
    targetStack.mLastPausedActivity = null;
    targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
    if (!launchTaskBehind) {
        // Don't set focus on an activity that's going to the back.
        mService.setFocusedActivityLocked(r, "startedActivity");
    }
    return ActivityManager.START_SUCCESS;
}

我们只分析和SingleTask模式相关的逻辑,已经在代码中使用注释说明:
1、判断当前Activity的启动模式是否是“栈内复用模式”。
2、在栈内复用模式下,分为两种情况。如果Activity所需要的任务栈存在。
3、栈内复用模式默认带有clearTop的属性,即它会找要启动的Activity,并移除要启动的Activity之上的所有Activity,然后调用ActivityRecord的deliverNewIntentLocked方法,该方法会最后调用Activity的onNewIntent方法。即最后会调用要启动的Activity的onNewIntent方法。
4、如果找不到要启动的Activity想要的任务栈,则重新创建一个任务栈,并创建Activity的实例,然后将Activity放入栈中。

1.4 SingleInstance:单实例模式

这是一种加强的SingleTask模式,它除了具有SingleTask模式的所有特性之外,还有所加强,那就是具有此模式的Activity只能单独地位于一个任务栈中。
举个例子,Activity A的启动模式是SingleInstance,当A启动后,系统会为它新建一个任务栈,然后A独自拥有这个新的任务栈。由于SingleInstance具有SingleTask模式的特性,也就是具有栈内复用特性,后续均不含创建新的Activity,除非这个独特的任务栈被系统销毁了。

二、启动模式的使用方式

2.1 在Manifest中注册

通过在AndroidManifest中指定启动模式,如下所示:


2.2 在Intent中设置标志位

在Intent中通过设置标志位可以为启动的Activity指定启动模式,如下所示:

Intent intent = new Intent();
intent.setClass(MainActivity.this, SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

上述两种方式都可以设置Activity的启动模式,但是两者还是存在着些许不同。
首先,当两种方式同时存在时,第二种方式的优先级要比第一种方式的优先级更高,所以会以第二种方式为准。
然后,第一种方式无法直接为Activity设置FLAG_ACTIVITY_CLEAR_TOP标识,而第二种方式无法为Activity设置SingleInstance启动模式。

三、Activity常见的标志位

Activity的标志位有很多,这里主要介绍一些在开发中比较常用的一些标志位。大部分情况下,我们是不需要为Activity指定标志位的,因此对于标志位理解即可。在使用标志位的时候,需要注意的是某些标志位是系统内部使用的,我们的App不需要手动设置这些标志位,否则会出现问题。

3.1 FLAG_ACTIVITY_NEW_TASK

这个标志位的作用是为Activity指定"SingleTask"的启动模式,相当于在Manifest中指定launchMode="singleTask"。

3.2 FLAG_ACTIVITY_SINGLE_TOP

这个标志位的作用是为Activity指定"SingleTop"的启动模式,相当于在Manifest中指定launchMode="singleTop"。

3.3 FLAG_ACTIVITY_CLEAR_TOP

这个标志位的作用是相当于将要启动的Activity所处的任务栈中,在它之上的Activity都要出栈。SingleTask具有的效果和该标志位相同。
如果被启动的Activity采用了standard的启动模式,那么它连同它之上的Activity都要出栈,然后系统会创建一个新的Activity实例并入栈。

四、启动模式的应用场景

4.1 Standard的应用场景

平时开发中使用最多的启动模式就是Standard,相信大家已经很熟悉了,这里就不再介绍。

4.2 SingleTop的应用场景

如果目前在栈顶中有一个Activity,这个时候还需要启动一个同类型的Activity,那么启动的Activity可以设置为SingleTop模式,这样不仅仅可以减少不必要的Activity的重建时间,并且可以节省App的内存。

4.3 SingleTask的应用场景

该模式最常用的场景就是在我们的应用中仅仅需要保持一个Activity实例。例如,我们经常将应用的主页(MainActivity)设置成SingleTask启动模式。这样就能保证在主页中回退时能够回到屏幕页面。

4.4 SingleInstance的应用场景

SingleInstance是全局单例模式,主要应用于系统中的应用,使得系统只有一个,一般我们在开发中不会用到该启动模式。

你可能感兴趣的:(【源码解析】Activity的启动模式)