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 ActivityResultexecStartActivity(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, ActivityRecordsourceRecord,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 activityif// 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<ReferrerIntent> 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<ReferrerIntent> intents) {final int N = intents.size();for (int i=0; i<N; i++) {ReferrerIntent intent = intents.get(i);intent.setExtrasClassLoader(r.activity.getClassLoader());intent.prepareToEnterProcess();r.activity.mFragments.noteStateNotSaved();mInstrumentation.callActivityOnNewIntent(r.activity, intent);}
}

在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, ActivityRecordsourceRecord,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中指定启动模式,如下所示:

<activity android:name="com.test.application.SecondActivity"android:launchMode=“singleTask" />

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的启动模式相关推荐

  1. Android 源码分析 Activity的启动模式

    一直想抽空针对AMS进行源码分析,无奈一方面因为很忙,另外AMS很复杂,涉及的知识点也比较多,今天利用五一假期对AMS的一个方面,Activity的启动模式进行源码分析,这里面包括了ActivityR ...

  2. Android8.0源码解析——Activity的启动过程

    前言 Activity是Android的四大组件,关于Activity 的启动过程是怎么样的昵,下面我们主要通过Android8.0的源码来分析一下. 1.Activity的生命周期: Activit ...

  3. 基于8.0源码解析:startService 启动过程

    基于8.0源码解析:startService 启动过程 首先看一张startService的图,心里有个大概的预估,跟Activity启动流程比,Service的启动稍微简单点,并且我把Service ...

  4. Android View体系(六)从源码解析Activity的构成

    前言 本来这篇是要讲View的工作流程的,View的工作流程主要指的measure.layout.draw这三大流程,在讲到这三大流程之前我们有必要要先了解下Activity的构成,所以就有了这篇文章 ...

  5. rocketmq源码解析之name启动(一)

    2019独角兽企业重金招聘Python工程师标准>>> 说在前面 主要解析namrsrv启动部分,namesrv配置加载.netty server创建.注册出处理器. 正文 源码解析 ...

  6. 6、RocketMQ 源码解析之 Broker 启动(上)

    上面一篇我们介绍了 RocketMQ 的元数据管理,它是通过自定义一个 KV 服务器.并且其它服务在 NameServer 注册服务信息的时候都是全量注册.如果 RocketMQ 的拓扑图当中有多台 ...

  7. 设计模式 结构型模式 -- 装饰者模式(概述 快餐店案例 模式优点 使用场景 源码解析 BufferedWriter 和代理模式的区别)

    1. 装饰者模式 1.1 概述 我们先来看一个快餐店的例子: 快餐店有炒面.炒饭这些快餐,可以额外附加鸡蛋.火腿.培根这些配菜,当然加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得 ...

  8. 源码分析-Activity的启动流程

    以android 6.0源码为参考,其他版本api会稍有不同 在Activity中,启动一个Activity的方法 @Override public void startActivity(Intent ...

  9. 【java】spring-boot源码解析之应用启动

    spring boot 项目使用默认配置的思想,极大的简化了 spring 项目的开发.下面的代码就是一个最简单的 spring 项目: @SpringBootApplication public c ...

  10. kotlin coroutine源码解析之Job启动流程

    目录 Job启动流程 launch流程分析 父子Job关联分析 结论 Job启动流程 job启动流程,我们先从一段最简单使用协程的代码开始,进行代码跟跟踪,顺便引出几个关键的概念,在后面章节里面去单独 ...

最新文章

  1. C++ :: 的用法小结
  2. Princess Principal(思维题)
  3. php怎么克隆,利用php怎么对对象进行克隆
  4. hive中操作struct与map三例
  5. mybatis一级缓存二级缓存
  6. 使用 jQuery Mobile 与 HTML5 开发 Web App (二) —— jQuery Mobile 基础
  7. 博文翻译系列——如何入门数据科学 without spending a penny
  8. 在线播放,将文件转成流媒体
  9. mysql可视化工具选型
  10. java源码社团管理系统_基于jsp的社团管理系统-JavaEE实现社团管理系统 - java项目源码...
  11. 计算机专业重要必修课程,计算机专业课程介绍
  12. html调用zblog文章,自定义调用ZBLOG分类页、内容页模板
  13. 多线程与单线程的区别
  14. 爱盛科技于中国电子展演示穿戴式装置地磁传感器方案
  15. 转载老码农教你学英语
  16. python裂缝检测_基于卷积神经网络的高楼外墙裂缝检测系统
  17. UVa-11212编辑书稿
  18. 论文解读:SentiPrompt: Sentiment Knowledge Enhanced Prompt-Tuning for Aspect-Based Sentiment Analysis
  19. 医学影像组学人工智能算法构建培训班
  20. 智能鸡舍环境监控系统设计思路

热门文章

  1. 【量化笔记】动量Momentum相关技术指标以其含义
  2. JS实现鼠标点击特效,五颜六色的小球绽放
  3. 黄金矿工~java小游戏【内含源码】
  4. oracle创建交叉表,SQL交叉表常用实例(转载网络)
  5. 《Spring Boot极简教程》附录1 计算机简史
  6. react 控制台 Maximum call stack size exceeded 如何解决
  7. 抗战史上知名的戚家刀PK日本真三武士刀刀型
  8. python-Selenium
  9. Word排版过程中多个参考文献一起引用
  10. android图片存储到本地文件夹在哪里找,android系统下,上网浏览的图片等临时文件的存放文件夹在哪?...