上一篇文章我们说到了startActivityUnchecked这个方法,这篇文章就接着这个方法开始说。这个方法的长度看上去可能还能接受,但是和长度相比,在我看来可能是AMS最复杂的一个方法了,从启动模式一直到开启新进程的入口,都是在这个方法中设计到。当然我们还是按照历史惯例,把代码分段来说,下面就开始第一部分的代码了。
```java
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
ActivityRecord[] outActivity) {
// 数据初始化
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor);
computeLaunchingTaskFlags(); // 继续处理启动的flag
// 获取调用进程的ActivityStack,保存在mSourceStack中
computeSourceStack();
mIntent.setFlags(mLaunchFlags); // 设置启动flag
// 这个方法是寻找有没有可以复用的Activity
ActivityRecord reusedActivity = getReusableIntentActivity();
```
这是第一部分代码,行数不多,不过里面有好几个方法,我们需要一个一个看。
# 初始化
第一个方法setInitialState是初始化,代码如下:
```java
// 做一些初始化的处理,比如获取displayid,启动模式,如果新任务栈,是否需要向老任务中对象发生result,是否这个activity启动后要马上resume
private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
boolean doResume, int startFlags, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
reset(); // 参数赋默认值
mStartActivity = r; // 将要启动的ActivityRecord
mIntent = r.intent; // 将要启动的intent
mOptions = options; // 一些Activity启动的属性,比如转场动画之类
mCallingUid = r.launchedFromUid; // 调用它的uid
mSourceRecord = sourceRecord; // 调用它的ActivityRecord
mVoiceSession = voiceSession;
mVoiceInteractor = voiceInteractor;
// 获取displayid
mSourceDisplayId = getSourceDisplayId(mSourceRecord, mStartActivity);
// 通过options获得尺寸
mLaunchBounds = getOverrideBounds(r, options, inTask);
mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP; // 是不是singleTop
mLaunchSingleInstance = r.launchMode == LAUNCH_SINGLE_INSTANCE; // 是不是singleInstance
mLaunchSingleTask = r.launchMode == LAUNCH_SINGLE_TASK; // 是不是singleTask
// 获取启动标签flag,比如new_task等待
mLaunchFlags = adjustLaunchFlagsToDocumentMode(
r, mLaunchSingleInstance, mLaunchSingleTask, mIntent.getFlags());
// mLaunchTaskBehind表示是否新建的Activity盖在原来的上面
mLaunchTaskBehind = r.mLaunchTaskBehind
&& !mLaunchSingleTask && !mLaunchSingleInstance
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
// 判断是否在一个新任务中启动,并且需要向老任务回复一个消息
// 如果是的话,就向老任务发送一个取消消息,并且把老的result对象置空
sendNewTaskResultRequestIfNeeded();
if ((mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
// If we are actually going to launch in to a new task, there are some cases where
// we further want to do multiple task.
if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
if (mLaunchTaskBehind
|| r.info.documentLaunchMode == DOCUMENT_LAUNCH_ALWAYS) {
mLaunchFlags |= FLAG_ACTIVITY_MULTIPLE_TASK;
}
}
// We'll invoke onUserLeaving before onPause only if the launching
// activity did not explicitly state that this is an automated launch.
// 如果是用户手动启动的,Intent.FLAG_ACTIVITY_NO_USER_ACTION为0,否则为1
mSupervisor.mUserLeaving = (mLaunchFlags & FLAG_ACTIVITY_NO_USER_ACTION) == 0;
if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
"startActivity() => mUserLeaving=" + mSupervisor.mUserLeaving);
// If the caller has asked not to resume at this point, we make note
// of this in the record so that we can skip it when trying to find
// the top running activity.
// doResume参数表示是否要求这个activity启动后要显示
mDoResume = doResume;
if (!doResume || !r.okToShowLocked()) {
// 如果启动后不要马上显示的话,下面2个参数设置下
// 这样寻找堆栈顶部Activity的时候,这个就被排除掉了
r.delayedResume = true;
mDoResume = false;
}
// 寻找mOptions所在的任务栈,然后看看如果这个任务栈是后台的,mDoResume还是要置false
if (mOptions != null && mOptions.getLaunchTaskId() != -1
&& mOptions.getTaskOverlay()) {
r.mTaskOverlay = true;
if (!mOptions.canTaskOverlayResume()) {
// 寻找任务栈
final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
final ActivityRecord top = task != null ? task.getTopActivity() : null;
// 任务栈中顶部的Activty现在不在前台,所以我们启动的这个也不要让他在前台?
if (top != null && top.state != RESUMED) {
// The caller specifies that we'd like to be avoided to be moved to the front,
// so be it!
mDoResume = false;
mAvoidMoveToFront = true;
}
}
}
// 是否是top
mNotTop = (mLaunchFlags & FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;
mInTask = inTask;
// In some flows in to this function, we retrieve the task record and hold on to it
// without a lock before calling back in to here... so the task at this point may
// not actually be in recents. Check for that, and if it isn't in recents just
// consider it invalid.
if (inTask != null && !inTask.inRecents) {
Slog.w(TAG, "Starting activity in task not in recents: " + inTask);
mInTask = null;
}
mStartFlags = startFlags;
// If the onlyIfNeeded flag is set, then we can do this if the activity being launched
// is the same as the one making the call... or, as a special case, if we do not know
// the caller then we count the current top activity as the caller.
// 调用者和启动者是不是同一个
if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
ActivityRecord checkedCaller = sourceRecord;
if (checkedCaller == null) {
checkedCaller = mSupervisor.mFocusedStack.topRunningNonDelayedActivityLocked(
mNotTop);
}
if (!checkedCaller.realActivity.equals(r.realActivity)) {
// Caller is not the same as launcher, so always needed.
mStartFlags &= ~START_FLAG_ONLY_IF_NEEDED;
}
}
mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0;
}
```
这个方法首先是先把一些之前传过来的参数保存在相关的变量中,之后调用getSourceDisplayId方法来获取调用者的屏幕,即displayId。看下这个方法:
```java
// 返回显示屏幕的id,如果调用者有displayid,就用调用者的。当然如果VR模式会用DEFAULT_DISPLAY
private int getSourceDisplayId(ActivityRecord sourceRecord, ActivityRecord startingActivity) {
// Check if the Activity is a VR activity. If so, the activity should be launched in
// main display.
if (startingActivity != null && startingActivity.requestedVrComponent != null) {
return DEFAULT_DISPLAY;
}
// Get the virtual display id from ActivityManagerService.
int displayId = mService.mVr2dDisplayId;
if (displayId != INVALID_DISPLAY) {
if (DEBUG_STACK) {
Slog.d(TAG, "getSourceDisplayId :" + displayId);
}
mUsingVr2dDisplay = true;
return displayId;
}
displayId = sourceRecord != null ? sourceRecord.getDisplayId() : INVALID_DISPLAY;
// If the activity has a displayId set explicitly, launch it on the same displayId.
if (displayId != INVALID_DISPLAY) {
return displayId;
}
return DEFAULT_DISPLAY;
}
```
从这个方法可以看到,这里考虑到了虚拟屏,如果是虚拟屏的话,比如投屏。如果启动的Activity的requestedVrComponent非空的话,会返回默认的屏幕,否则如果
使用AMS中提供的displayId。如果不是虚拟屏的话,会从调用者这里获取displayId。
接着会获取minifest中的启动模式,比如single_top,single_instance,single_task等。下面会调用adjustLaunchFlagsToDocumentMode这个方法来做一些启动模式的调整,先看下这个方法:
```java
// 获取启动标签flag,比如new_task等待
// r是要启动的activity
// launchSingleInstance是是否是singleInstance
// launchSingleTask是是否是singleTask
// launchFlags是从intent中获得的启动标签
private int adjustLaunchFlagsToDocumentMode(ActivityRecord r, boolean launchSingleInstance,
boolean launchSingleTask, int launchFlags) {
// 启动标签如果和minifest中有冲突,以minifest为准
// 下面启动标签FLAG_ACTIVITY_NEW_DOCUMENT表示一个Activity可以存在多个任务中
// 而后面single...标签确只能在一个任务中,所以冲突了,以minifest为准
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 | FLAG_ACTIVITY_MULTIPLE_TASK);
} else {
// 到这里说明FLAG_ACTIVITY_NEW_DOCUMENT为0,single都是false
switch (r.info.documentLaunchMode) {
case ActivityInfo.DOCUMENT_LAUNCH_NONE:
break;
case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
break;
case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
break;
case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
launchFlags &= ~FLAG_ACTIVITY_MULTIPLE_TASK;
break;
}
}
return launchFlags;
}
```
在说这个方法之前,我们先说FLAG_ACTIVITY_NEW_DOCUMENT这个启动模式。对于启动模式一般的四种相信大家都比较熟悉了,但是FLAG_ACTIVITY_NEW_DOCUMENT一般我们使用的不多。这个启动模式用于在同一个activity实例中,我们需要每次启动在一个新的task中,一般常用的四大启动模式要么重复启动一个新在一个task中,要么启动一个单例在不同的task中,用FLAG_ACTIVITY_NEW_DOCUMENT这个启动模式,我们既可以启动在不同的task中,并且不是单例的启动方法,即让同一个Activity实例存在在多个不同的task中。当然这里有个条件,需要加上FLAG_ACTIVITY_MULTIPLE_TASK标签,不过这样才能实现在多个task中存在,否则就还是只有1个实例,这点需要注意。
我们看下这个方法对他是怎么处理的。首先如果flag是带FLAG_ACTIVITY_NEW_DOCUMENT的,同时minifest里是single_instance或者single_task,根据上面介绍的,FLAG_ACTIVITY_NEW_DOCUMENT是一个实例会在多个task中存在,而single_instance或者single_task是只会存在一份实例,那么他们就冲突了,那么以哪个为准呢?这里的处理是以minifest为准。
以上是通过Intent中设置flag的形式,还可以通过minifest的形式来设置,在minifest中是通过documentLaunchMode标签来设置的,上面代码中的else分支就是处理documentLaunchMode标签的,可以看到一共有四种:
1. DOCUMENT_LAUNCH_NONE:这个就表示什么都不设置
2. DOCUMENT_LAUNCH_INTO_EXISTING和DOCUMENT_LAUNCH_ALWAYS:从代码看这两个都会加入FLAG_ACTIVITY_NEW_DOCUMENT的标志,根据前面介绍的,我们知道只有在加上FLAG_ACTIVITY_MULTIPLE_TASK标志,FLAG_ACTIVITY_NEW_DOCUMENT才会起作用,所以这里只是先加上FLAG_ACTIVITY_NEW_DOCUMENT,如果是DOCUMENT_LAUNCH_ALWAYS则会在后面的代码中加上,我们马上就会看到。而DOCUMENT_LAUNCH_INTO_EXISTING从名字看,就是从现有中寻找一个task加入,所以只要加上FLAG_ACTIVITY_NEW_DOCUMENT就可以了。
3. DOCUMENT_LAUNCH_NEVER: 这里的代码是把FLAG_ACTIVITY_MULTIPLE_TASK关闭了,所以表示不会开启在多个task中存在同样的Activity实例。
这个方法主要是处理FLAG_ACTIVITY_NEW_DOCUMENT这个标志,由于平常这个标志我们用的不多,所以这个方法重点讲一下。我们回到前面setInitialState方法,接下去说。mLaunchTaskBehind表示是否盖在一个Activity上面,如果同时FLAG_ACTIVITY_NEW_DOCUMENT标志是打开的,同时非single_task和single_instance,那么mLaunchTaskBehind被置为true。这个在处理显得代码中会用到。
接着调用了sendNewTaskResultRequestIfNeeded这个方法,我们看下实现:
```java
private void sendNewTaskResultRequestIfNeeded() {
final ActivityStack sourceStack = mStartActivity.resultTo != null
? mStartActivity.resultTo.getStack() : null;
// 如果这个将要启动的Activity目前是一个任务栈中,并且将要以一个新任务栈来启动
// 这样在两个不同进程就比较乱了,所以现在向这个老的栈中发一个取消的消息
// 并且把现在的result的对象置空
if (sourceStack != null && (mLaunchFlags & 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.
sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
null /* data */);
mStartActivity.resultTo = null;
}
}
,
```
这里可以看到resultTo是需要回复的Activity,这里先取出他所在的task,如果他的task存在,并且FLAG_ACTIVITY_NEW_TASK也是置位的,这样就产生问题了,task存在表示回复需要向这个task回复,而FLAG_ACTIVITY_NEW_TASK又表示需要寻找一个新的task,从google的注释中可以看出,google对这种处理也感觉有点乱,既然在一个新的task中建立了Activity,然而完成后又要向老的Activy所在task中回复消息,这样可能实现起来就非常复杂了,所以google这里做了简单处理直接给老的Activity回复了一个cancel,然后就把回复的Activity置空了。
然后回到前面代码继续往下看,如果FLAG_ACTIVITY_NEW_DOCUMENT被置位了,同时没有回复的Activity,那么就加上FLAG_ACTIVITY_NEW_TASK。
然后再进一步处理,前面我们说到了minifes中设置了DOCUMENT_LAUNCH_ALWAYS这个的时候,已经加上了FLAG_ACTIVITY_NEW_DOCUMENT,现在会判断如果FLAG_ACTIVITY_NEW_TASK也加上了,那么就在加上FLAG_ACTIVITY_MULTIPLE_TASK,这样前面我们介绍的DOCUMENT_LAUNCH_ALWAYS这个标签功能就完整了,他会在每次启动这个Activity的时候都开启一个新的task。
然后根据前面传过来的doResume来设置是否需要显示。接着如果指定了taskId的话,会根据这个taskId获取task,进而获取栈顶的Activity,然后根据这个Activity当前的状态再设置下mDoResume和mAvoidMoveToFront,这两个表示是否要显示以及是否不要在前台显示。
接着START_FLAG_ONLY_IF_NEEDED这个标志表示调用者和启动的目标是不是同一个,如果不是同一个的话并且调用者是空的话,我们就获取当前焦点栈最上面一个正常显示的Activity,然后再判断一下这个目前显示的Activity和目标要启动的Activity是不是同一个,如果不是的话就把START_FLAG_ONLY_IF_NEEDED置0。
# 初步处理taskRecord
至此初始化的方法分析完了,主要是设置了当前一些变量的状态保存下,供后续的使用。我们接着看后面computeLaunchingTaskFlags这个方法:
```java
//主要处理启动的flag,看到底有没有新的任务栈可以加,还是要开新栈
private void computeLaunchingTaskFlags() {
// 不是从一个Activity中启动的,但是给了一个任务栈
if (mSourceRecord == null && mInTask != null && mInTask.getStack() != null) {
final Intent baseIntent = mInTask.getBaseIntent(); // 获取这个taskRecord第一个加入activity的Intent或者affinityIntent
final ActivityRecord root = mInTask.getRootActivity(); // 获取这个TaskRecord中root Activity
if (baseIntent == null) { // 这个任务栈必须放过Activity
ActivityOptions.abort(mOptions);
throw new IllegalArgumentException("Launching into task without base intent: "
+ mInTask);
}
// 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 (mLaunchSingleInstance || mLaunchSingleTask) {
// 如果是这2种,必定找到的这个taskrecord的intent,和目标的intent相同
// 因为是复用任务栈,并且都是single,所以第一个应该相同,但是mLaunchSingleTask为什么要是root?
// 我的理解,AMS不适合在这里校验往这个task中插入activity是否正确,耗时且非AMS的职责
// 所以这个只做最简单且正确的处理
if (!baseIntent.getComponent().equals(mStartActivity.intent.getComponent())) {
ActivityOptions.abort(mOptions);
throw new IllegalArgumentException("Trying to launch singleInstance/Task "
+ mStartActivity + " into different task " + mInTask);
}
// 都这里说明上面的判断都通过了,root activity的componenet和启动的这个应该是一样的
// 所以这里的root就是要启动的activity,由于都是single的所以不能再往里面插入activity了
// 会报错
if (root != null) {
ActivityOptions.abort(mOptions);
throw new IllegalArgumentException("Caller with mInTask " + mInTask
+ " 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.
// 到这里说明任务栈是有的,但是里面没有Activity,所以等于现在目标Activity是第一个加入的Activity
if (root == null) {
final int flagsOfInterest = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK
| FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS;
mLaunchFlags = (mLaunchFlags & ~flagsOfInterest)
| (baseIntent.getFlags() & flagsOfInterest);
mIntent.setFlags(mLaunchFlags);
mInTask.setIntent(mStartActivity);
mAddingToTask = true;
// 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 ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
// 到这里说明root有东西,而且是指定的task,又是new_task,按照注释说明最好是一个root activity
// 有东西了,就不能root了,所以表示不往这个栈中添加
mAddingToTask = false;
} else {
// 到这里,有任务栈,而且不是NEW_TASK,所以可以加入
mAddingToTask = true;
}
mReuseTask = mInTask;
} else { // 到这里没有任务栈或者有调用者
mInTask = null;
// Launch ResolverActivity in the source task, so that it stays in the task bounds
// when in freeform workspace.
// Also put noDisplay activities in the source task. These by itself can be placed
// in any task/stack, however it could launch other activities like ResolverActivity,
// and we want those to stay in the original task.
// 启动的Activity是ResolverActivit或者不显示的Activity会走这个if
if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null
&& mSourceRecord.isFreeform()) {
mAddingToTask = true;
}
}
..............
```
这个方法我们分两段来看,方法的主要功能是计算一个要启动的任务栈task。这里分为两种情况一种是有指定要启动的任务栈task,一种是没有指定。我们先看上面这段代码。
首先会做一下过滤,如果这个task的intent是空的话,会报错了。task的intent就是第一个加入这个task的Activity的intent,或者是affinityIntent,affinityIntent是如果一个task需要更改他的root activity的时候,会把intent置空,设置affinityIntent为更改后activity的intent。设置这个置等于是给一个task做了一个命名,在后面的代码中我们还可以看到在寻找一个合适的task过程中也会利用这个intent来比较,这对于像single_instance这种一个任务栈只有一个元素的task来说,查找起来就比如容易过滤了,这个在后面我们就可以看到。
如果intent非空的话,接着会判断single_instance和single_task这两种模式,具体的处理是判断这个task的intent.component和启动的Activity的component是否一样。由于single_instance是单独占用一个任务栈的,所以如果提供了一个不一样的话,肯定要报错。但是如果是single_task是可以存在一个多个activity的task中的,为什么这里也要强制只有一样才能加入呢?我自己的理解是,这是为了防止用户传入了一个错误的task给系统,如果此时存在一个task中有要启动的activity,但是这里传入的确不是这个task,那么不就出问题了吗,当然在代码这里也可以通过重新查找来判断传入的这个是不是正确,但是这个判断查找还是比较耗时的,做这个校验的话对所有的启动activity流程就都要做这个处理了,所以肯定是不合适的,而且这个工作也不应该由AMS这里才处理,所以无论从性能上还是职责上都不会做这样的检验,所以就强制要求无论是single_instance还是single_task,如果指定了一个task,必须要componenet相同。
检验了component后,在判断下这个root activity,如果root activity不空,那么就说明知道task现在的已经存在目标要启动的activity了,又要启动一个single_instance或者single_task的activity,所以这也是属于错误的行为,这个直接返回了。
经过上面的处理后,如果root activity为空的话,那么说明这个task是空了,那么我们就新建一个activity加入这个task中。如果root activity不是空,经过上面的判断,能进入到这里肯定不是single_instance或者single_task,并且启动标志中有FLAG_ACTIVITY_NEW_TASK,说明可能会要寻找一个新的task来加入,所以会把mAddingToTask置为false,表示具体的task还未确定。其他情况就直接可以往这个指定的task中加入了。
如果是有调用者或者没有指定具体的task的情况下,会进一步判断,如果是ResolverActivity的话或者调用者在自由显示的模式下,也会加入指定的task。ResolverActivity就是前面已经介绍过的,比如一个分享弹出的界面,里面包含了多个匹配的应用。自由模式就是在桌面上可以随意拖动的模式。这个了解一下就好。我们继续看这个方法的最后一点代码:
```java
if (mInTask == null) { // 当前没有指定的任务栈
// 到这里说明有可能有调用者,也可能没有调用者
if (mSourceRecord == null) { // 也没调用者
// This activity is not being started from another... in this
// case we -always- start a new task.
if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {
Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
"Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
} else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) { // 调用者启动模式是singleInstance,那么被调用者不能和他在一起,也要在一个新的里面
// The original activity who is starting us is running as a single
// instance... this new activity it is starting must go on its
// own task.
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
} else if (mLaunchSingleInstance || mLaunchSingleTask) { // 目标启动模式是single_instance获取single_task,也会加上new_task
// The activity being started is a single instance... it always
// gets launched into its own task.
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
// 如果以上3个分支都没进,说明有调用者,但是没指定任务栈,这种情况最常见的一种情况,比如进程内的调用
// 同时调用者不是single_instance,被调用者是single_top或者standard
}
```
这段方法比较容易理解,如果没有指定任务栈的情况下,没有调用者或者调用者是single_instance模式,启动的activity是single_instance或者single_task模式,会都加上FLAG_ACTIVITY_NEW_TASK,接下去我们会看到FLAG_ACTIVITY_NEW_TASK加上后会进一步寻找适合的task以及activityStack,这里把这几种情况都加上表示后面的方法里面会继续寻找。
# 初步处理ActivityStack
除了这几种情况外,其余的情况基本属于不用寻找一个复用的任务栈,比如延用当前进程调用者所在的task等等,等于是已经找到了一个要加入的task了,后面直接处理加入的逻辑就好,后面再加入的时候也会看到。目前我们分析到这里,除了这种已经找到的情况外,主要还是要处理还未确定task的情况,下面就会寻找那些还未确定的task。在继续寻找未确定的task之前,还有一个逻辑要处理下,也是一种task未确定的情况,看下面方法:
```java
//获取调用进程的ActivityStack,保存在mSourceStack中。如果调用进程正在finish,那么就没有调用进程了信息了,置空
private void computeSourceStack() {
if (mSourceRecord == null) {
mSourceStack = null;
return;
}
if (!mSourceRecord.finishing) { // 有调用者,那么就保存下调用者的ActivityStack
mSourceStack = mSourceRecord.getStack();
return;
}
// 如果走到这里,说明上面mSourceRecord正在被finish,那么我们不能继续用他的任务栈,需要开辟一个新任务栈。
if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0) { // 不是NEW_TASK
Slog.w(TAG, "startActivity called from finishing " + mSourceRecord
+ "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
mNewTaskInfo = mSourceRecord.info;
final TaskRecord sourceTask = mSourceRecord.getTask();
mNewTaskIntent = sourceTask != null ? sourceTask.intent : null; // 获取原来任务栈顶的intent或者是null
}
// 由于调用者被finish了,所以这里置空
mSourceRecord = null;
mSourceStack = null;
}
```
这个方法是获取调用者的ActivityStack。如果调用者是null,那么就直接返回了,后面还会计算调用者的ActivityStack。如果调用者当前没有被finish,那么获取他的ActivityStack后也会return。
如果调用者当前正在被finish了,并且启动模式也没有FLAG_ACTIVITY_NEW_TASK标志,那么此时调用者的task也不确定能不能使用,所以就考虑寻找一个当前合适的task来启动这个新的Activity,所以会把启动模式flag添加上FLAG_ACTIVITY_NEW_TASK,当然会把原来调用者的Activity信息和task信息保存在mNewTaskInfo和mNewTaskIntent中。最后有调用者马上就要被finish了,所以把mSourceRecord和mSourceStack置空。
# 寻找复用任务栈
至此,所有task未确定的情况都有了,下面我们就开始在现有的栈中寻找是否有可以复用的,如果可以复用的那就那些为确定的task情况就可以直接用,否则就只有开辟一个新任务栈来使用了,下面我们看下寻找复用任务栈的方法:
```java
/**
* Decide whether the new activity should be inserted into an existing task. Returns null
* if not or an ActivityRecord with the task into which the new activity should be added.
*/
// 这个方法是寻找有没有可以复用的Activity
// 其实这个Activity也不一定是复用的Activity,但是他对应的taskRecord肯定是复用的
private ActivityRecord getReusableIntentActivity() {
// 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.
// putIntoExistingTask为true,表示新 Activity 需要放到放到现有任务栈中,条件是:
// 1. Flags 包含 FLAG_ACTIVITY_NEW_TASK 但是不包含 FLAG_ACTIVITY_MULTIPLE_TASK
// FLAG_ACTIVITY_MULTIPLE_TASK表示全局可以有多个任务,为0就说明这个flag是全局单例的
// 2. launchMode 是 singleInstance 或 singleTask
boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
(mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| mLaunchSingleInstance || mLaunchSingleTask;
// 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.
// mInTask为空,说明没有指定一个taskRecord要加入,所以可能有一个task可以复用,所以允许寻找putIntoExistingTask=true
// resultTo == null说明不需要回复,那么也就是可以寻找一个符合条件的task复用。否则如果非null,必须要和调用者同一个栈,也就没必要寻找复用栈了
putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
ActivityRecord intentActivity = null;
// 这里分为两个方面看。第一个if进去表示有指定一个任务栈,else则是没指定
// 1. 启动模式是一个singleInstance,通过在所有屏幕中找到任务栈,然后在通过对比目标activity的component来确定是否找的到这个activity
// 2. 分屏模式和singleInstance一样,区别就是singleInstance比较的是Activity的component,分屏模式比较intent
// 3. 不是singleInstance,也不是分屏模式,比如是singlTask这种
if (mOptions != null && mOptions.getLaunchTaskId() != -1) { // 这种情况下,说明明确需要插入到这个任务栈,但是应该lunchMode不能是singleInstance 或 singleTask
// 根据stackId寻找一个任务栈
final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
// 如果task不为空,说明找到了,那么就返回这个任务栈的栈顶元素,后续会根据不同条件做处理
// 反正是要往这个taskRecord中插入Activity的,至于顶部这是不是后面判断
intentActivity = task != null ? task.getTopActivity() : null;
} else if (putIntoExistingTask) {
// 进入这里说明是single_instance,single_task,new_task
if (mLaunchSingleInstance) { // 进这里是single_instance
// There can be one and only one instance of single instance activity in the
// history, and it is always in its own unique task, so we do a special search.
// 由于是single_instance,一个任务栈就只有一个对象,所以这里比较component就可以区分出是不是复用的了
intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info, false);
} else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) { // 分屏模式
// For the launch adjacent case we only want to put the activity in an existing
// task if the activity already exists in the history.
intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
!mLaunchSingleTask);
} else {
// 比如new_task或者singletask,由于new_task可以包含所有启动模式,所以这里可以理解为,standard,single_top,singletask都有可能
// 这里只比较taskRecord,看看哪个taskRecord的affinity,intent,或者affinityIntent是一样的,说明找到一个复用的任务栈,返回这个栈最上面一个Activity
// 因为这3种启动模式可能在复用栈中已经有很多的activity了,所以通过上面比较先拿到栈,至于栈中有没有,有的话怎么处理,放在后面再执行。
// Otherwise find the best task to put the activity in.
intentActivity = mSupervisor.findTaskLocked(mStartActivity, mSourceDisplayId);
}
}
return intentActivity;
}
```
这个方法首先根据FLAG_ACTIVITY_NEW_TASK有设置同时FLAG_ACTIVITY_MULTIPLE_TASK无设置,或者single_instance或者single_top这几种情况都是属于可以寻找一个复用的任务栈的,这里需要注意的是FLAG_ACTIVITY_MULTIPLE_TASK这个标志是不能有的,前面有过分析,如果有这个标志是会存在同一个实例在多个任务栈中存在的情况,那也没必要寻找重复栈了,这里提一下,具体这几种情况经过前面的分析,相信大家应该都可以理解。
另外mInTask为空,表示没有指定任务栈。mStartActivity.resultTo为空,表示不需要返回调用者。这2个只有为空,才能寻找一个复用的任务栈。对于不需要返回调用者为什么要为空呢,我的理解是,之前分析sendNewTaskResultRequestIfNeeded方法的时候,有分析到如果需要返回调用者,需要在同一个任务栈中,如果跨栈的话,google的处理就是直接发回一个cancel消息就完事了,否则太复杂了,所以这个只有回复者为空才会寻找一个复用栈,不空就说明在同一个任务栈中了。
这个方法返回的是一个ActivityRecord,代表了这个栈中的一个Activity,可能就是启动元素需要的那个Activity,即复用Activity,比如处理single_instance这种情况。也可能只是复用任务栈中的栈顶元素,其本身并非是复用的Activity,只不过通过他来确定了一个复用的task。
接下往下看,首先如果有指定taskId,那么我们通过anyTaskForIdLocked方法找到这个TaskRecord,然后返回它的栈顶ActivityRecord,这里具体可以看代码,不是很复杂,就不多分析了。
如果没有指定taskId,并且putIntoExistingTask是true的话,那么就开始寻找是否有可以复用的任务栈了,下面分了三种情况,single_instance,分屏模式和其余。
single_instance和分屏模式的调用方法是一样的,都是调用findActivityLocked方法来寻找复用栈,我们来具体看下这个方法:
```java
// 在任务栈中寻找ActivityRecord
// 参数compareIntentFilters为true比较intent,false比较componenet
ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
boolean compareIntentFilters) {
// 在所有屏幕中遍历
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
// 获取每个屏幕的ActivityStack集合
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
// 遍历ActivityStack集合
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
// 在ActivityStack中寻找ActivityRecord
final ActivityRecord ar = stacks.get(stackNdx)
.findActivityLocked(intent, info, compareIntentFilters);
if (ar != null) {
return ar;
}
}
}
return null;
}
```
这个方法遍历每个屏幕,然后再遍历屏幕的每个ActivityStack,在调用ActivityStack的findActivityLocked方法寻找ActivityRecord,我们继续看这个方法:
```java
ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
boolean compareIntentFilters) {
// 获取包名和类名
ComponentName cls = intent.getComponent();
if (info.targetActivity != null) {// targetActivity非空,说明是个alias
// 重新拼装ComponentName
cls = new ComponentName(info.packageName, info.targetActivity);
}
// 获取useid
final int userId = UserHandle.getUserId(info.applicationInfo.uid);
// 遍历TaskRecord集合
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
// 获取TaskRecord
final TaskRecord task = mTaskHistory.get(taskNdx);
// 获取ActivityRecord集合
final ArrayList<ActivityRecord> activities = task.mActivities;
// 遍历ActivityRecord集合
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = activities.get(activityNdx);
if (!r.okToShowLocked()) { // 如果是不被显示给用户的,则continue
continue;
}
if (!r.finishing && r.userId == userId) {
if (compareIntentFilters) {
if (r.intent.filterEquals(intent)) {
return r;
}
} else {
// 比较component是否相同,相同就返回
if (r.intent.getComponent().equals(cls)) {
return r;
}
}
}
}
}
return null;
}
```
这个方法也是同样的逻辑,遍历这个ActivityStack的所有ActivityRecord,根据传进来的参数比较他们的intent获取Component,如果找到相同的就返回这个ActivityRecord,否则返回null。可以看到这种方法是需要遍历每一个ActivityRecord来寻找是否有复用的任务栈的。
说过了前两种寻找复用栈的方法,我们看下第三种。我们先看下,能执行到这第三种的情况有哪些启动模式。前面我们说过只有FLAG_ACTIVITY_NEW_TASK并且非FLAG_ACTIVITY_MULTIPLE_TASK的情况,还有single_task或single_instance才会进行寻找复用任务栈,前面single_instance已经在上面执行过了,剩下的就是single_task或带FLAG_ACTIVITY_NEW_TASK并且非FLAG_ACTIVITY_MULTIPLE_TASK这两种情况了,后面这种情况其实包含了standard和single_top,所以其实常用的四种启动模式,除了single_instance,其余三种都有可能,而且这三种所存在的task中,除了包含我们需要寻找的Activity,都有存在其他Activity的可能,所以如果还是按照遍历每个ActivityRecord来寻找的话,就太耗时了,所以调用的方法是findTaskLocked,从命令可以看出来这里是寻找一个task,和前面调用的方法名字findActivityLocked相比较,我们大概可以直观的了解他们的区别,一个是寻找Activity,另一个是寻找task。好了,我们看下具体的方法实现:
```java
ActivityRecord findTaskLocked(ActivityRecord r, int displayId) {
mTmpFindTaskResult.r = null;
mTmpFindTaskResult.matchedByRootAffinity = false;
ActivityRecord affinityMatch = null;
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
// 遍历所有ActivityDIsplay
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
// 获取每个ActivityDisplay中的ActivityStack
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
// 遍历ActivityStack
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
// 判断栈类型是否相同
if (!checkActivityBelongsInStack(r, stack)) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) "
+ stack);
continue;
}
if (!stack.mActivityContainer.isEligibleForNewTasks()) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS,
"Skipping stack: (new task not allowed) " + stack);
continue;
}
// 在这个Activity中寻找目标Activity是否存在,如果存在会保存在mTmpFindTaskResult的r中
stack.findTaskLocked(r, mTmpFindTaskResult);
if (mTmpFindTaskResult.r != null) {
if (!mTmpFindTaskResult.matchedByRootAffinity) {
return mTmpFindTaskResult.r; // 这种情况是比较精确的比较,可以直接确认有这个ActivityRecord,直接返回
} else if (mTmpFindTaskResult.r.getDisplayId() == displayId) {
affinityMatch = mTmpFindTaskResult.r; // 这种情况再比较下displayId才能返回
}
}
}
}
if (DEBUG_TASKS && affinityMatch == null) Slog.d(TAG_TASKS, "No task found");
return affinityMatch;
}
```
这个方法同样先是遍历屏幕后,先获取一个ActivityStack,然后调用checkActivityBelongsInStack判断下这个ActivityStack和Activity的类型是否相同,
```java
private boolean checkActivityBelongsInStack(ActivityRecord r, ActivityStack stack) {
if (r.isHomeActivity()) {
// 桌面stack
return stack.isHomeStack();
} else if (r.isRecentsActivity()) {
// 最近任务stack
return stack.isRecentsStack();
} else if (r.isAssistantActivity()) {
// 语言助手
return stack.isAssistantStack();
}
return true;
}
```
可以看到这里就判断了三种类型,桌面,最近任务和语音助手,其他的比如分屏之类的,都是属于可以复用的情况里面的,所以目前这里不需要过滤掉。
我们回到前面,继续调用findTaskLocked方法寻找可以复用的任务栈:
```java
// 在任务栈TaskRecord中寻找ActivityRecord
void findTaskLocked(ActivityRecord target, FindTaskResult result) {
Intent intent = target.intent; // 获取目标intent
ActivityInfo info = target.info; // 获取目标ActivityInfo
ComponentName cls = intent.getComponent(); // 获得component
if (info.targetActivity != null) { // 如果是alisas,重新拼装
cls = new ComponentName(info.packageName, info.targetActivity);
}
final int userId = UserHandle.getUserId(info.applicationInfo.uid);
boolean isDocument = intent != null & intent.isDocument();
// If documentData is non-null then it must match the existing task data.
Uri documentData = isDocument ? intent.getData() : null;
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + target + " in " + this);
// 遍历TaskRecord集合,由于只要比较task的intent或者affinity或者intent就可以知道是不是需要的taskRecord
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
// 拿到每个TaskRecord
final TaskRecord task = mTaskHistory.get(taskNdx);
if (task.voiceSession != null) {
// We never match voice sessions; those always run independently.
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": voice session");
continue;
}
if (task.userId != userId) { // 判断userId是否相同
// Looking for a different task.
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": different user");
continue;
}
// 取出栈顶Ativity,先判断栈顶这个元素空不空,是不是一个用户等情况
final ActivityRecord r = task.getTopActivity();
// 一些异常的就跳过,LAUNCH_SINGLE_INSTANCE由于进入这个方法的时候,已经排除的这个情况
// 所以跳过
if (r == null || r.finishing || r.userId != userId ||
r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch root " + r);
continue;
}
//
if (r.mActivityType != target.mActivityType) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch activity type");
continue;
}
...........................
```
这个方法我们也分两段来说。这里第一段,先获取要启动的Activity的ComponentName等信息,然后遍历每个TaskRecord,然后做一些过滤的判断,比如userId是否相同,并且不能是single_instance,这个很重要,一方面前面我们已经计算过了single_instance这种情况了,另一方面,singel_instance会带了一个副作用,即同一个affinity的名字会存在多个,这样对后面我们计算其他启动模式就会产生问题,这个接下去我们会说。再之后就取出这个taskRecord的栈顶元素,这里就和之前寻找是否有可以复用的Activity不同了,之前是遍历所有的ActivityRecord后比较,这里只要获取栈顶一个Activity就可以了,具体怎么比较的,我们接着后半段代码来看,这里取出栈顶的ActivityRecord后,同样先做一些过滤的判断,接着我们看后半段代码:
```java
// 取出task的一些信息来比较
final Intent taskIntent = task.intent; // 获取栈顶intent
final Intent affinityIntent = task.affinityIntent;
final boolean taskIsDocument;
final Uri taskDocumentData;
if (taskIntent != null && taskIntent.isDocument()) {
taskIsDocument = true;
taskDocumentData = taskIntent.getData();
} else if (affinityIntent != null && affinityIntent.isDocument()) {
taskIsDocument = true;
taskDocumentData = affinityIntent.getData();
} else {
taskIsDocument = false;
taskDocumentData = null;
}
.............................
// 比较task的intent
if (taskIntent != null && taskIntent.getComponent() != null &&
taskIntent.getComponent().compareTo(cls) == 0 &&
Objects.equals(documentData, taskDocumentData)) {
// 栈顶的intent一样,那就找到了
...................
result.r = r;
result.matchedByRootAffinity = false;
break;
// 比较task的affinityIntent,affinityIntent一般在reparent时候的task设置,此时intent为null
} else if (affinityIntent != null && affinityIntent.getComponent() != null &&
affinityIntent.getComponent().compareTo(cls) == 0 &&
Objects.equals(documentData, taskDocumentData)) {
// 是个affinityIntent,component也一样,找到
.................
result.r = r;
result.matchedByRootAffinity = false;
break;
} else if (!isDocument && !taskIsDocument
&& result.r == null && task.rootAffinity != null) {
// 比较taskAffinity
if (task.rootAffinity.equals(target.taskAffinity)) {
// 上面2种比较由于是比较component对象,可以很精确的确认是不是一个Activiy
// 而这种情况只能说明任务栈名字一样,由于排除了多任务的存在,所以这里affinity
result.r = r;
result.matchedByRootAffinity = true;
}
} else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
}
}
```
这段代码是计算是否有可以复用的任务栈。先是获取taskIntent,affinityIntent,每个TaskRecord的intent是在这个任务栈新建的时候,有第一个加入的Activity的intent决定的,即root activity决定的。而当一个taskRecord要改变的时候,也会改变这个taskRecord的intent,即会把affinityIntent赋值为新的root activity的intent,而之前的intent置为空了。所以这里寻找一个可以复用的任务栈的时候,会先比较是否有intent或者affinityIntent是否相同,如果相同的话,就说明和这个栈的root activity是一样的,就直接break了。如果没找到相同的,那我们比较一下,他们的affinity是否相同,如果是相同的话会先记录下来,但是不break,然后继续遍历,直到找到一个intent一样的,或者全部遍历完成后结束。
可能有的同学会问,就这样每次只遍历任务栈中的一个ActivityRecord,会有问题吗?我们可以进一步深入思考一下,首先比较intent和affinityIntent,如果一样就直接返回,这样肯定是没问题的,既然能找到intent相同的肯定是确定的一个一样的Activity,那么我们就找到了。关键是最后这个计算任务栈的affinity会不会有重复或者加入了一个不对的task中呢?
我目前的理解是,就目前代码来看一般情况下不会。首先前面进入这个方法的前提条件是非FLAG_ACTIVITY_MULTIPLE_TASK的,所以不会存在同一个Activity存放在多个task中的情况。其次上面我们说了,现在代码执行到这里已经排除了single_instance这种情况了,因为他会带来一个问题,由于他需要自己单独占用一个任务栈,所以可能会存在有多个相同affinity名字的任务栈,这里既然已经排除了这种情况在结合上面排除FLAG_ACTIVITY_MULTIPLE_TASK的情况,那么目前每个任务栈的affinity名字都是唯一的,所以只要相等就可以保存下来。
# 存在复用任务栈的处理
执行到这里我们就找到了一个复用任务栈了,我们回到最前面的startActivityUnchecked方法,继续看后面的代码:
```java
// 下面不为空的话,根据上面getReusableIntentActivity方法的逻辑
// 有四种情况,要么option指定了一个taskId
// 或者是new_task,single_task,single_instance
// 说明找到了一个可以复用的任务栈
if (reusedActivity != null) { // 如果找到一个复用的任务栈,这个是任务栈里面的Activity
// 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.
// task是锁定的,即不被清除的,但是又要求清除task,报错
if (mSupervisor.isLockTaskModeViolation(reusedActivity.getTask(),
(mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
mSupervisor.showLockTaskToast();
Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
if (mStartActivity.getTask() == null) {
// 目标进程的ActivityRecord设置下任务栈
mStartActivity.setTask(reusedActivity.getTask());
}
if (reusedActivity.getTask().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.
// 这个下这个复用的Activity对应任务栈的intent
reusedActivity.getTask().setIntent(mStartActivity);
}
// This code path leads to delivering a new intent, we want to make sure we schedule it
// as the first operation, in case the activity will be resumed as a result of later
// operations.
// 下面标志需要清除任务栈顶部或者new document或者是singleInstance或者singleTask,这四种情况可能都需要清除栈顶元素(mLaunchSingleInstance反正就一个也算啦)
// FLAG_ACTIVITY_CLEAR_TOP会清空该activity之上所有元素,不包括他
if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isDocumentLaunchesIntoExisting(mLaunchFlags)
|| mLaunchSingleInstance || mLaunchSingleTask) {
// 获取前面计算出的复用的任务栈
final TaskRecord task = reusedActivity.getTask();
// 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.
// 清除和mStartActivity一样的元素之上的所以Activity,
// 如果那个一样的元素是standard,并且mLaunchFlags有single_top的话,也会被清除掉
final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
mLaunchFlags);
// The above code can remove {@code reusedActivity} from the task, leading to the
// the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The
// task reference is needed in the call below to
// {@link setTargetStackAndMoveToFrontIfNeeded}.
if (reusedActivity.getTask() == null) {
reusedActivity.setTask(task);
}
if (top != null) { // 这里的top就是上面清除顶部所有元素后,留下来的那个,即和目标是一样的那个
if (top.frontOfTask) { // 如果是任务栈中,现在这个是根Activity,设置下任务栈的intent
// 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.getTask().setIntent(mStartActivity);
}
// 打印一些这个ActivityRecord的信息,比如userId,taskId,Compoment等
ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask());
// 向这个Activity发送onNewIntent。上面performClearTaskForReuseLocked中,由于如果是找到相同的那个
// ACtivity是standard启动模式,并且现在启动的这个Activity的flag没有single_top,会把那个activity给finish掉
// 所以现在能执行到这里说明不是空,会复用这个Activity,所以要调用它的onNewIntent
top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
mStartActivity.launchedFromPackage);
}
}
sendPowerHintForLaunchStartIfNeeded(false /* forceSend */);
// 经过上面的处理,目前任务栈taskRecord已经找到了,里面如果有复用的Activity也已经把他上面的元素清掉了(如果需要的话)
// 现在需要把taskRecord和ActivityStack分别移动到他们对应的集合的最前面
reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);
final ActivityRecord outResult =
outActivity != null && outActivity.length > 0 ? outActivity[0] : null;
// When there is a reused activity and the current result is a trampoline activity,
// set the reused activity as the result.
if (outResult != null && (outResult.finishing || outResult.noDisplay)) {
outActivity[0] = reusedActivity;
}
// 调用者和目标是同一个,不用启动一个新的Activity,调用onresume
if ((mStartFlags & 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.
resumeTargetStackIfNeeded();
return START_RETURN_INTENT_TO_CALLER;
}
// 上面的setTargetStackAndMoveToFrontIfNeeded方法把task和stack放到前面了
// 这里的方法会继续处理几种flag的情况,比如single_task,single_instance,single_top
setTaskFromIntentActivity(reusedActivity);
// mReuseTask只有在开始option就指定了一个任务栈,或者找到一个复用任务栈并且flag是clear_task时候才会有值
// 所以这里表示没有复用栈,并且就是有找到一个复用栈但是要求清空,
if (!mAddingToTask && mReuseTask == 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.
resumeTargetStackIfNeeded();
if (outActivity != null && outActivity.length > 0) {
outActivity[0] = reusedActivity;
}
return START_TASK_TO_FRONT;
}
}
```
上面这段代码,是在找到一个可以复用的任务栈情况下执行的。开始还是过滤一些特殊情况,比如任务栈是锁定的,即不允许被清除,但是启动模式中又要求FLAG_ACTIVITY_CLEAR_TASK清除栈,所以会返回报错。之后设置下启动的任务栈,然后更新下复用栈的intent。接下去要处理的四种情况都是把当前要启动的这个Activity放在任务栈的顶部。四种情况分别是FLAG_ACTIVITY_CLEAR_TOP,FLAG_ACTIVITY_NEW_DOCUMENT和非FLAG_ACTIVITY_MULTIPLE_TASK,single_instance,singel_task,这四种启动模式在完成,当前的这个Activity就会存在加入的那个任务栈的顶端,我们看下处理的方法:
```java
// 清除newR这个元素(即一个Activity)之上的所有Activity
ActivityRecord performClearTaskForReuseLocked(ActivityRecord newR, int launchFlags) {
mReuseTask = true;
final ActivityRecord result = performClearTaskLocked(newR, launchFlags);
mReuseTask = false;
return result;
}
```
继续跟进performClearTaskLocked方法:
```java
// 这里进来的singleTask,singleInstance,clear_top
// 下面方法就是找到和新加入的Activity一样的那个ActivityRecord,然后这3种情况,都把找到的之上的清除掉就可以了
final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
// 获取ActivityRecord的数量
int numActivities = mActivities.size();
// 从后往前遍历ActivityRecord
for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
// 获取每个ActivityRecord
ActivityRecord r = mActivities.get(activityNdx);
if (r.finishing) {
continue;
}
// 确认component是一样的
if (r.realActivity.equals(newR.realActivity)) {
// Here it is! Now finish everything in front...
final ActivityRecord ret = r;
// 从找到的这个Activity上面一个开始,到顶部,不包含本身
for (++activityNdx; activityNdx < numActivities; ++activityNdx) {
r = mActivities.get(activityNdx);
if (r.finishing) {
continue;
}
ActivityOptions opts = r.takeOptionsLocked();
if (opts != null) {
ret.updateOptionsLocked(opts);
}
// 找到的这个Activity之上的所有元素,不包括本身
if (mStack != null && mStack.finishActivityLocked(
r, Activity.RESULT_CANCELED, null, "clear-task-stack", false)) {
--activityNdx;
--numActivities;
}
}
// Finally, if this is a normal launch mode (that is, not
// expecting onNewIntent()), then we will finish the current
// instance of the activity so a new fresh one can be started.
// r是找到的那个和将要启动的Activity一样的元素,
// 上面如果执行完毕的话,那个r上面的元素都finish了
// 那么r现在这个要不要finish,如果r的启动模式是standard,并且目标启动flag没有设置是single_top,那么也会把这个相同的Activity给finish
// 这里LAUNCH_MULTIPLE就是standard启动模式
if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
&& (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0
&& !ActivityStarter.isDocumentLaunchesIntoExisting(launchFlags)) {
if (!ret.finishing) {
if (mStack != null) {
mStack.finishActivityLocked(
ret, Activity.RESULT_CANCELED, null, "clear-task-top", false);
}
return null;
}
}
return ret;
}
}
return null;
}
```
这个方法整体逻辑还是比较简单的,遍历任务栈中所有的ActivityRecord,跳过正在finish的,直到找到有复用的ActivityRecord为止,先把他记录下来,因为后面有一种情况是需要把这个复用的也给删除的,我们下面马上就会讲到。在这个复用的Activity之上的Activity会调用finish方法把他删除,finish方法会走正常的AMS和客户端交互的流程,这个我们在后面说到Activity最终的几个和生命周期相关的方法的时候会一起说,这里只要了解是把Activity给finish掉了就可以了。最后更新下这个TaskRecord保存的Activity的数量就可以了。
正常情况下,如果有复用的Activity,这个找到后,把他放置到任务栈最前面后,会回调onNewIntent方法,但是有种情况即使存在复用的Activity,也会把这个Activity给finish掉,然后重新加入,最后一段代码就是这种情况。这里这个栈中找到的复用的Activity启动模式是ActivityInfo.LAUNCH_MULTIPLE,这个不要理解为多任务,他的值是0,这个相当于standard启动模式,同时新启动的Activity的启动模式非FLAG_ACTIVITY_SINGLE_TOP,也不是FLAG_ACTIVITY_NEW_DOCUMENT启动模式,那么在这种情况下,这个复用的Activity会被finish掉,后面会重新创建这个Activity加入这个栈。这个就是特殊处理的一个情况,如果不看源码的话,如果遇到和这个相关的bug,估计头也比较大。好了我们回到startActivityUnchecked方法,继续后面的流程。
上面方法会返回当前栈顶的元素top,如果存在复用的话,会调用它的deliverNewIntentLocked方法,走生命周期的onNewIntent方法,这个现在也不细说,后面说到生命周期的时候再说。
上面这部分代码,都是处理了再加入一个任务栈后,可能会涉及到需要清楚栈中原本一些存在的Activity的情况。现在这些情况都处理好了,我们继续往下看,现在复用的这个任务栈是找到了,接着要考虑它的ActivityStack了,只有ActivityStack+taskRecord两者都确定了,才能真正确定最终的添加Activity的位置,下面setTargetStackAndMoveToFrontIfNeeded这个方法主要是计算ActivityStack的:
```java
// 设置目标的任务栈,并且移动到最前面
private ActivityRecord setTargetStackAndMoveToFrontIfNeeded(ActivityRecord intentActivity) {
mTargetStack = intentActivity.getStack(); // 获取要插入的任务栈
mTargetStack.mLastPausedActivity = null;
// 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.
// 获取当前焦点ActivityStack
final ActivityStack focusStack = mSupervisor.getFocusedStack();
// 获取当前焦点ActivityRecord
ActivityRecord curTop = (focusStack == null)
? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
// 获取当前焦点栈顶的TaskRecord,这个是当前前台ActivityStack的taskRecord,不一定是目标要插入的taskRecord
final TaskRecord topTask = curTop != null ? curTop.getTask() : null;
```
这里是第一部分代码,这个方法的参数就是复用的那个Activity。我们首先获取他所在的ActivityStack以及当前焦点所在的ActivityStack,如果焦点ActivityStack不为空,获取他最上面正常显示的Activity所在的TaskRecord,即这里可以称为焦点task。好,这段代码没什么东西,我们继续看下一段代码:
```java
// 1. 当前焦点任务栈非空
// 2. 同时焦点task和要复用的目标任务栈不是同一个或者虽然是同一个但不是ActivityStack的TaskRecord集合的顶部
// 3. 可以被移动到前台
// 这里既然焦点的任务栈和目标任务栈不是同一个,肯定是需要把目标任务栈给放到最前面,下面会处理
// 这里第二个条件,如果当前焦点task和目标task是同一个的话,也不用处理了,当前task已经在最前面了,所以不一样才会进入这里分支继续处理
// 另外如果是一样的话,但是不是在顶端,这种情况由于目前是最新的调用了,所以需要把这个task放到顶端,所以这种情况也要处理
if (topTask != null
&& (topTask != intentActivity.getTask() || topTask != focusStack.topTask())
&& !mAvoidMoveToFront) {
mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); // 表示这个Activity是移动上来的
// 如果调用者是空
// 或者调用者所在ActivityStack顶部Activity所在的task和调用者的task是同一个,即调用者的task需要再他stack的顶部
// 这里如果调用者是null,那么会把当前这个task放到前台。
// 如果调用者是存在的,但是他也要在前台,才会把目标task调用到前台。这个上面的注释中有看到
if (mSourceRecord == null || (mSourceStack.topActivity() != null &&
mSourceStack.topActivity().getTask() == mSourceRecord.getTask())) {
// We really do want to push this one into the user's face, right now.
if (mLaunchTaskBehind && mSourceRecord != null) {
intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
}
mMovedOtherTask = true;
// If the launch flags carry both NEW_TASK and CLEAR_TASK, the task's activities
// will be cleared soon by ActivityStarter in setTaskFromIntentActivity().
// So no point resuming any of the activities here, it just wastes one extra
// resuming, plus enter AND exit transitions.
// Here we only want to bring the target stack forward. Transition will be applied
// to the new activity that's started after the old ones are gone.
// 是否要清理任务栈,这里有FLAG_ACTIVITY_CLEAR_TASK
final boolean willClearTask =
(mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
if (!willClearTask) { // 不需要清理
// 获取启动ActivityStack
final ActivityStack launchStack = getLaunchStack(
mStartActivity, mLaunchFlags, mStartActivity.getTask(), mOptions);
// 获取启动的TaskRecord,如果前面有计算出复用的会取复用的,否则根据其他情况获取
final TaskRecord intentTask = intentActivity.getTask();
// 如果要启动的为空,(launchStack == mTargetStack应该是相当的)那就把复用的TaskRecord放到最前面来
if (launchStack == null || launchStack == mTargetStack) {
// We only want to move to the front, if we aren't going to launch on a
// different stack. If we launch on a different stack, we will put the
// task on top there.
// 移动目标taskRecord和ActivityStack到最前面,即移动intentTask到mTargetStack顶部
mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions,
mStartActivity.appTimeTracker, "bringingFoundTaskToFront");
mMovedToFront = true; // 标记说明正在移动一个taskRecord
} else if (launchStack.mStackId == DOCKED_STACK_ID
|| launchStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) { // 分屏
// If we want to launch adjacent and mTargetStack is not the computed
// launch stack - move task to top of computed stack.
intentTask.reparent(launchStack.mStackId, ON_TOP,
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"launchToSide");
} else {
// TODO: This should be reevaluated in MW v2.
// We choose to move task to front instead of launching it adjacent
// when specific stack was requested explicitly and it appeared to be
// adjacent stack, but FLAG_ACTIVITY_LAUNCH_ADJACENT was not set.
// 移动目标taskRecord到最前面
mTargetStack.moveTaskToFrontLocked(intentTask,
mNoAnimation, mOptions, mStartActivity.appTimeTracker,
"bringToFrontInsteadOfAdjacentLaunch");
}
mMovedToFront = true; // 标记
} else if (launchStack.mDisplayId != mTargetStack.mDisplayId) { // 找到的启动屏幕和目标屏幕不一样
// Target and computed stacks are on different displays and we've
// found a matching task - move the existing instance to that display and
// move it to front.
// 把目标taskRecord移动到指定的stack中,即参数一的ActivityStack
intentActivity.getTask().reparent(launchStack.mStackId, ON_TOP,
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"reparentToDisplay");
mMovedToFront = true;
}
mOptions = null;
// We are moving a task to the front, use starting window to hide initial drawn
// delay.
intentActivity.showStartingWindow(null /* prev */, false /* newTask */,
true /* taskSwitch */);
}
updateTaskReturnToType(intentActivity.getTask(), mLaunchFlags, focusStack);
}
}
// 如果进入上面的分支,把mMovedToFront设置为true了,里面的方法会处理把mTargetStack设置为焦点stack,所以这里就不用处理了
// 如果没有设置,这个就把目标stack放到前台
if (!mMovedToFront && mDoResume) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + mTargetStack
+ " from " + intentActivity);
mTargetStack.moveToFront("intentActivityFound");
}
mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(), INVALID_STACK_ID,
DEFAULT_DISPLAY, mTargetStack.mStackId);
// If the caller has requested that the target task be reset, then do so.
if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
return mTargetStack.resetTaskIfNeededLocked(intentActivity, mStartActivity);
}
return intentActivity;
}
```
我们知道启动的Activity是放在一个TaskRecord中的,taskRecord是在一个ActivityStack中的,当前是找到一个复用的taskRecord,这个taskRecord肯定是需要把他调整到ActivityStack的顶部的,这样才能在前台显示,但是这个taskRecord目前所在的ActivityStack不一定就是最终的那个ActivityStack,最终的ActivityStack可能调用者已经指定了,也可能是当前的焦点ActivityStack,或者是不同屏幕中的ActivityStack,所以还需要进一步确认最终的ActivityStack,找到后需要把最终的ActivityStack调整到最前面,我们看看google这里是怎么处理的。
首先有个判断条件,上面代码注释中也有写到:
1.当前焦点ActivityStack顶部的taskRecord非空
2.当前焦点ActivityStack顶部taskRecord和当前复用taskRecord不一样,或者虽然一样,但是他们不是所在Activity的顶部
3. 复用的Activitystack是运行被移动到最前面的
可以看到如果以上三个条件同时都满足的话,会进一步处理,这个接下来就会说。如果不满足的话,那么当前复用taskRecord要么就是当前的焦点taskRecord,那么他们的ActivityStack肯定也是一样的,要么当前焦点taskRecord是空,那么复用的ActivityStack还是继续延用目前taskRecord所在的。所以当不满足上面3点条件的时候,只需要把复用taskRecord所在的ActivityStack移动到最前面就可以,我们先看这个处理的方法:
```java
void moveToFront(String reason) {
moveToFront(reason, null);
}
// 把当前ActivityStack放到同一屏幕下的最顶端
// 设置为焦点ActivityStack
void moveToFront(String reason, TaskRecord task) {
if (!isAttached()) {
return;
}
mStacks.remove(this); // 看看集合中有没有要插入的这个activityStack,有的话移除
mStacks.add(findStackInsertIndex(ON_TOP), this); // 插入这个ActivityStack到同一个displayId的ActvityStack集合 *** 把ActivityStack放在同一个displayId的顶部
mStackSupervisor.setFocusStackUnchecked(reason, this); // 设置当前这个activityStack为焦点ActivityStack ***这里是把ActivityStack设置为焦点了
if (task != null) {
// *** 把task放到Activity的顶部
insertTaskAtTop(task, null); // 把这个taskRecord插入到这个ActivityStack中task集合的顶部
return;
}
task = topTask();
if (task != null) {
mWindowContainerController.positionChildAtTop(task.getWindowContainerController(),
true /* includingParents */);
}
}
```
最终会调到有2个参数的moveToFront方法。这个方法首先把自己从同一个屏幕下的所有ActivityStack集合中删除,然后再加入到这个集合的顶部,最后如果这个ActivityStack设置为焦点。这样当前的复用ActivityStack就处理好了,我们在回过头看看,如果不能确定是当前这个复用taskRecord所在的ActivityStack的时候,是如何确定ActivityStack的
从代码中可以看到,如果调用者为空或者调用者非空并且在前台的话,并且不是FLAG_ACTIVITY_CLEAR_TASK的情况下,会调用getLaunchStack方法再去计算一下要启动的ActivityStack,我们看下这个方法:
```java
// 获取要启动的ActivityStack
private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, TaskRecord task,
ActivityOptions aOptions) {
// We are reusing a task, keep the stack!
// 如果有指定的task,返回他的ActivityStack,前面computeLaunchingTaskFlags中会赋值
if (mReuseTask != null) {
return mReuseTask.getStack();
}
// If the activity is of a specific type, return the associated stack, creating it if
// necessary
// 如果activity是以下几种类型的,比如桌面的,最近任务的,等等。这里类型有特定的ActivityStack,返回这些特定的
if (r.isHomeActivity()) {
return mSupervisor.mHomeStack;
}
if (r.isRecentsActivity()) {
return mSupervisor.getStack(RECENTS_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
}
if (r.isAssistantActivity()) {
return mSupervisor.getStack(ASSISTANT_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
}
// 以下是从ActivityOptions中获取displayId和ActivityStackId
// 返回调用者指定的displayId
final int launchDisplayId =
(aOptions != null) ? aOptions.getLaunchDisplayId() : INVALID_DISPLAY;
// 返回调用者指定的ActivityStackId
final int launchStackId =
(aOptions != null) ? aOptions.getLaunchStackId() : INVALID_STACK_ID;
// 下面是一些校验
if (launchStackId != INVALID_STACK_ID && launchDisplayId != INVALID_DISPLAY) {
throw new IllegalArgumentException(
"Stack and display id can't be set at the same time.");
}
// 验证下ActivityStackId是否有效,如果有效,则根据这个StackId返回ActivtyStack。也即
// 根据option提供的stackId返回ActivityStack
if (isValidLaunchStackId(launchStackId, launchDisplayId, r)) {
return mSupervisor.getStack(launchStackId, CREATE_IF_NEEDED, ON_TOP);
}
if (launchStackId == DOCKED_STACK_ID) {
// The preferred launch stack is the docked stack, but it isn't a valid launch stack
// for this activity, so we put the activity in the fullscreen stack.
return mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
}
// 根据option提供的displayId获取ActivityStack
if (launchDisplayId != INVALID_DISPLAY) {
// Stack id has higher priority than display id.
return mSupervisor.getValidLaunchStackOnDisplay(launchDisplayId, r);
}
// If we are using Vr2d display, find the virtual display stack.
if (mUsingVr2dDisplay) {
ActivityStack as = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r);
if (DEBUG_STACK) {
Slog.v(TAG, "Launch stack for app: " + r.toString() +
", on virtual display stack:" + as.toString());
}
return as;
}
if (((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0)
|| mSourceDisplayId != DEFAULT_DISPLAY) {
return null;
}
```
这里mReuseTask是在开始初始化方法中赋值的,如果通过的初始化那里的校验,这个指定的taskRecord就是有效的,那么就直接使用他的ActivityStack。否则就根据要启动的这个Activity是什么类型的,比如桌面,最近任务,语言助手等,他们都有单独的ActivityStack,所以如果启动的Activity是这几种类型的话,那么需要把taskRecord加入到这些ActivityStack中。然后,如果调用者提供了displayId或者ActivityStackId的话,看下启动的Activity是否和这些中的ActivtyStack匹配,有匹配的话就返回。最后如果启动模式不是分屏模式,或者不是当前主屏ID,就返回null。如果启动是在一个分屏下的stack,下面继续看代码:
```java
// 到这里处理分屏模式
// 这个task就是前面计算的复用的task。所以这个如果task非空的话,直接返回对应的ActivityStack
// 如果目标Activity的TaskRecord非空,则获取他的ActivityStack
// 否则获取目标Activiy的ActivityContain的ActivtyStack
// 如果上面都是null,取当前焦点的ActivityStack
final ActivityStack parentStack = task != null ? task.getStack()
: r.mInitialActivityContainer != null ? r.mInitialActivityContainer.mStack
: mSupervisor.mFocusedStack;
// 如果从目标Activity中获得的ActivityStack,不是当前焦点,则用parentStack
if (parentStack != mSupervisor.mFocusedStack) {
// If task's parent stack is not focused - use it during adjacent launch.
return parentStack;
} else { // 目标Activity中取得ActivityStack和当前焦点是一个
//判断下如果当前焦点的栈顶任务栈和目标是一个,说明用的是同一个任务栈,返回他所在的ActivityStack
if (mSupervisor.mFocusedStack != null && task == mSupervisor.mFocusedStack.topTask()) {
// If task is already on top of focused stack - use it. We don't want to move the
// existing focused task to adjacent stack, just deliver new intent in this case.
return mSupervisor.mFocusedStack;
}
// 到这里说明当前聚焦栈就是分屏的,所以我们放在分屏的另一边
if (parentStack != null && parentStack.isDockedStack()) {
// If parent was in docked stack, the natural place to launch another activity
// will be fullscreen, so it can appear alongside the docked window.
return mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED,
ON_TOP);
} else { // 聚焦栈不是分屏的,由于启动模式又是哟分屏的,那么我们检查下分屏stack能不能显示这个Activity
// If the parent is not in the docked stack, we check if there is docked window
// and if yes, we will launch into that stack. If not, we just put the new
// activity into parent's stack, because we can't find a better place.
// 获得分屏stack
final ActivityStack dockedStack = mSupervisor.getStack(DOCKED_STACK_ID);
// 分屏不能用不能用,返回null
if (dockedStack != null
&& dockedStack.shouldBeVisible(r) == STACK_INVISIBLE) {
// There is a docked stack, but it isn't visible, so we can't launch into that.
return null;
} else { // 分屏stack能用
return dockedStack;
}
}
}
}
```
经过前面的处理,这里一段代码主要是寻找分屏的ActivityStack了。首先这里的task是启动Activity目前找到的复用taskRecord,如果非空的话,返回他所在的ActivityStack。否则的话,看下启动ActivityRecord的mInitialActivityContainer变量是否为空,这个是在创建ActivityRecord是否赋值的,也是调用者通过参数传过来的,不过我们平时常见的这个null为主。如果这个不为空的话,就返回他的ActivityStack,否则最后只有使用当前的焦点ActivityStack了。
接着,如果现在启动的这个ActivityStack不是当前焦点,那就直接返回。如果和焦点是同一个的话,并且复用的taskRecord是焦点的顶部,那么就直接返回焦点ActivityStack。否则既然启动要放在分屏的stack中,那就看看是不是DockedStack这个,DockedStack是分屏的一半,另一半就是下面看到的FullScreenStack了。如果不是DockedStack,那就返回FullScreenStack。如果是DockedStack,那么先获取DockedStack,看看能不能用,如果能用就返回他,不能用就返回null。
好了,回到setTargetStackAndMoveToFrontIfNeeded,我们现在已经计算出了要启动的ActivityStack,然后再结合复用taskRecord所在的ActivityStack,我们最终可以确认一个ActivityStack,我们看下代码,由于离开上面有段距离了,这里我再贴下代码:
```java
if (!willClearTask) { // 不需要清理
// 获取启动ActivityStack
final ActivityStack launchStack = getLaunchStack(
mStartActivity, mLaunchFlags, mStartActivity.getTask(), mOptions);
// 获取启动的TaskRecord,如果前面有计算出复用的会取复用的,否则根据其他情况获取
final TaskRecord intentTask = intentActivity.getTask();
// 如果要启动的为空,(launchStack == mTargetStack应该是相当的)那就把复用的TaskRecord放到最前面来
if (launchStack == null || launchStack == mTargetStack) {
// We only want to move to the front, if we aren't going to launch on a
// different stack. If we launch on a different stack, we will put the
// task on top there.
// 移动目标taskRecord和ActivityStack到最前面,即移动intentTask到mTargetStack顶部
mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions,
mStartActivity.appTimeTracker, "bringingFoundTaskToFront");
mMovedToFront = true; // 标记说明正在移动一个taskRecord
} else if (launchStack.mStackId == DOCKED_STACK_ID
|| launchStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) { // 分屏
// If we want to launch adjacent and mTargetStack is not the computed
// launch stack - move task to top of computed stack.
intentTask.reparent(launchStack.mStackId, ON_TOP,
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"launchToSide");
} else { // 这里是指定的
// TODO: This should be reevaluated in MW v2.
// We choose to move task to front instead of launching it adjacent
// when specific stack was requested explicitly and it appeared to be
// adjacent stack, but FLAG_ACTIVITY_LAUNCH_ADJACENT was not set.
// 移动目标taskRecord到最前面
mTargetStack.moveTaskToFrontLocked(intentTask,
mNoAnimation, mOptions, mStartActivity.appTimeTracker,
"bringToFrontInsteadOfAdjacentLaunch");
}
mMovedToFront = true; // 标记
} else if (launchStack.mDisplayId != mTargetStack.mDisplayId) { // 找到的启动屏幕和目标屏幕不一样
// Target and computed stacks are on different displays and we've
// found a matching task - move the existing instance to that display and
// move it to front.
// 把目标taskRecord移动到指定的stack中,即参数一的ActivityStack
intentActivity.getTask().reparent(launchStack.mStackId, ON_TOP,
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"reparentToDisplay");
mMovedToFront = true;
}
mOptions = null;
// We are moving a task to the front, use starting window to hide initial drawn
// delay.
intentActivity.showStartingWindow(null /* prev */, false /* newTask */,
true /* taskSwitch */);
}
```
首先,如果计算出的启动ActivityStack是空,或者和复用的ActivityStack一样,那就使用复用的ActivityStack,调用moveTaskToFrontLocked方法把ActivtyStack和taskRecord调到最前面,我们看下方法实现:
```java
// 移动TaskRecord到所在ActivityStack中的顶部,即这里的参数一tr
final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, ActivityOptions options,
AppTimeTracker timeTracker, String reason) {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
// 获取当前这个ActivityStack所在屏幕的顶部ActivityStack
final ActivityStack topStack = getTopStackOnDisplay();
// 获取这个ActivityStack的顶部Activity
final ActivityRecord topActivity = topStack != null ? topStack.topActivity() : null;
// 这个ActivityStack的task数量
final int numTasks = mTaskHistory.size();
// 从activityStack中寻找这个TaskRecord
final int index = mTaskHistory.indexOf(tr);
// 如果没找到就返回
if (numTasks == 0 || index < 0) {
// nothing to do!
if (noAnimation) {
ActivityOptions.abort(options);
} else {
updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
}
return;
}
if (timeTracker != null) {
// The caller wants a time tracker associated with this task.
for (int i = tr.mActivities.size() - 1; i >= 0; i--) {
tr.mActivities.get(i).appTimeTracker = timeTracker;
}
}
// Shift all activities with this task up to the top
// of the stack, keeping them in the same internal order.
// *** 到这里说明是找到了需要提到最前面的taskRecord了,把目标的TaskRecord插入ActivityStack顶部
insertTaskAtTop(tr, null);
// Don't refocus if invisible to current user
// 获取这个插入taskRecord的栈顶activity
final ActivityRecord top = tr.getTopActivity();
// 如果栈顶是null,或者不被显示的Activity,那么插入到最近的任务栈中,假装已经调用过了
if (top == null || !top.okToShowLocked()) {
addRecentActivityLocked(top);
ActivityOptions.abort(options);
return;
}
// Set focus to the top running activity of this stack.
// 获取该activityStack顶部的ActivityRecord
final ActivityRecord r = topRunningActivityLocked();
// 这里就是处理ActivityStack了
// 1. 设置r的activityStack放到ActivityStack集合中的前部,这个集合都是同一个displayId
// 2. 设置r的activityStack为焦点ActivityStack
// 3. 设置r的taskRecord调整到task集合的顶部,即调用insertTaskAtTop,上面已经调用过,不确定是不是有重复
mStackSupervisor.moveFocusableActivityStackToFrontLocked(r, reason);
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
// 一些动画的处理
if (noAnimation) {
mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
if (r != null) {
mNoAnimActivities.add(r);
}
ActivityOptions.abort(options);
} else {
updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
}
// If a new task is moved to the front, then mark the existing top activity as supporting
// picture-in-picture while paused
if (topActivity != null && topActivity.getStack().getStackId() != PINNED_STACK_ID) {
topActivity.supportsPictureInPictureWhilePausing = true;
}
// 调用顶部Activity的resume。到这里目标Activity还没加入到任务栈中
mStackSupervisor.resumeFocusedStackTopActivityLocked();
EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
mService.mTaskChangeNotificationController.notifyTaskMovedToFront(tr.taskId);
}
```
这个方法首先把复用的这个taskRecord调整到所在ActivityStack的顶部,具体方法是调用insertTaskAtTop,比较简单就不展开了。接着要把对应的ActivityStack调到顶部,调用的方法是moveFocusableActivityStackToFrontLocked,这个我们跟进去看一下:
```java
boolean moveFocusableActivityStackToFrontLocked(ActivityRecord r, String reason) {
if (r == null || !r.isFocusable()) {
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS,
"moveActivityStackToFront: unfocusable r=" + r);
return false;
}
final TaskRecord task = r.getTask(); // 获取这个Activiy的taskRecord
final ActivityStack stack = r.getStack(); // 获取这个Activity的ActivityStack
if (stack == null) {
Slog.w(TAG, "moveActivityStackToFront: invalid task or stack: r="
+ r + " task=" + task);
return false;
}
// 如果当前需要放在前台的activity对应的activityStack已经在前台了
// 并且顶部运行的Activity也是现在需要的的那个,那么就说明已经都在最前面了,不需要处理了
if (stack == mFocusedStack && stack.topRunningActivityLocked() == r) {
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS,
"moveActivityStackToFront: already on top, r=" + r);
return false;
}
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS,
"moveActivityStackToFront: r=" + r);
// 把当前ActivityStack放到同一屏幕下的最顶端
// 设置为焦点ActivityStack
stack.moveToFront(reason, task);
return true;
}
```
该方法开始做一些错误的校验,这个不多说。然后如果当前这个ActivityStack已经是焦点stack了,并且栈顶Activity就是启动的Activity,那么也不用做什么操作。如果都不是的话,继续调用moveToFront方法,这个方法前面已经介绍过了。到这里ActivityStack,TaskRecord,ActivityRecord就都已经安排好了。
我们回到setTargetStackAndMoveToFrontIfNeeded方法,上面说了计算出的启动ActivtyStack和复用的ActivityStack那么就会使用复用的ActivityStack来调整。接着如果两者不一样的话,如果要加入分屏的ActivityStack,并且启动模式也是分屏模式,要么会调用reparent来把复用的taskRecord移动他计算出的ActivityStack中。如果要加入的是分屏ActivityStack,但是是调用时候指定的参数,那么说明此时的taskRecord也是指定的,那么他肯定已经在一个分屏的ActivityStack中了,我们只要和前面一样,调用moveTaskToFrontLocked方法把他们都调整到最顶端就可以了。我们主要来看下reparent方法,怎么把一个taskRecord移动到其他ActivityStack中的:
```java
// 把该task移动到指定ActivityStack中,即下面的preferredStackId
boolean reparent(int preferredStackId, boolean toTop, @ReparentMoveStackMode int moveStackMode,
boolean animate, boolean deferResume, String reason) {
return reparent(preferredStackId, toTop ? MAX_VALUE : 0, moveStackMode, animate,
deferResume, true /* schedulePictureInPictureModeChange */, reason);
}
```
这里参数一就是需要加入的ActivityStack的id,我们继续跟进去:
```java
// 把该task移动到指定ActivityStack中,即下面的preferredStackId。当然要把task从原来的ActivityStack中移除
boolean reparent(int preferredStackId, int position, @ReparentMoveStackMode int moveStackMode,
boolean animate, boolean deferResume, boolean schedulePictureInPictureModeChange,
String reason) {
final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
final WindowManagerService windowManager = mService.mWindowManager;
final ActivityStack sourceStack = getStack(); // 复用taskRecord的ActivityStack
// 获取目标的ActivityStack,目标ActivityStackId即preferredStackId
final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStackId,
position == MAX_VALUE);
if (toStack == sourceStack) { // 复用ActivityStack和目标ActivityStack不能一样
return false;
}
......................................
// Adjust the position for the new parent stack as needed.
position = toStack.getAdjustedPositionForTask(this, position, null /* starting */);
..................
final boolean moveStackToFront = moveStackMode == REPARENT_MOVE_STACK_TO_FRONT
|| (moveStackMode == REPARENT_KEEP_STACK_AT_FRONT && (wasFocused || wasFront));
// Move the task
// 从复用ActivityStack中移除这个task
sourceStack.removeTask(this, reason, moveStackToFront
? REMOVE_TASK_MODE_MOVING_TO_TOP : REMOVE_TASK_MODE_MOVING);
// 把这个task加入目标ActivityStack
toStack.addTask(this, position, false /* schedulePictureInPictureModeChange */, reason);
...................
return successful;
}
```
由于代码比较长,这里只贴出相关的,其余的可以自己再看下代码。可以看到,首先这里也做一些校验,如果复用的计算出的ActivityStack是同一个,那么就返回了,这个就不需要移动taskRecord了。
之后通过getAdjustedPositionForTask获取需要插入ActivityStack的位置,我们看下这个方法:
```java
// 获取插入位置
// 把当前这个要启动的Activity对应的找到的taskRecord插入到这个activitystack中taskRecord的集合顶部或适当位置
// 会根据当前这个task要不要正常显示
int getAdjustedPositionForTask(TaskRecord task, int suggestedPosition,
ActivityRecord starting) {
int maxPosition = mTaskHistory.size();
// 由于正常启动一个activity的时候starting会传null,同时如果这个Activity是可以正常显示的。会走这里的分支
if ((starting != null && starting.okToShowLocked())
|| (starting == null && task.okToShowLocked())) {
// If the task or starting activity can be shown, then whatever position is okay.
return Math.min(suggestedPosition, maxPosition); // 取得插入位置
}
// The task can't be shown, put non-current user tasks below current user tasks.
// 如果这个task不能显示情况下,获取从上到下,第一个这种taskRecord的位置
while (maxPosition > 0) {
final TaskRecord tmpTask = mTaskHistory.get(maxPosition - 1);
// 获取第一个从上到下非当前用户的taskRecord或者获取从上到下第一个taskRecord里面的activity目前都不能显示的
// 即要把这个task放在第一个也是不显示task的上面,返回这个位置
if (!mStackSupervisor.isCurrentProfileLocked(tmpTask.userId)
|| tmpTask.topRunningActivityLocked() == null) {
break;
}
maxPosition--;
}
return Math.min(suggestedPosition, maxPosition);
}
```
如果这个taskRecord能正常显示的情况下,这里取历史任务栈中最后一个位置,可以看到这里会计算一个最小值Math.min(suggestedPosition, maxPosition),这里suggestedPosition是一个非常大的值MAX_VALUE = 0x7fffffff,所以一般情况下是取任务栈中的最后一个位置。
如果这个task是不要显示的,那么要从上到下寻找第一个也是不显示的或者是第一个非本用户的taskRecord,因为这个task是不显示的,那么插入的位置肯定要在显示的那些下面,否则就挡住别人正常显示的了,另外由于是最新插入的,那么必须要在所有不显示的taskRecord上面,所以寻找第一个不显示的位置返回。
回到reparent方法,找到了要插入的位置,那么接下来就是2步,第一步从原ActivityStack中移除,第二步加入新的ActivityStack。我们先看第一步移除:
```java
// 从ActivityStack中移除task
void removeTask(TaskRecord task, String reason, int mode) {
// 遍历这个taskrecord中所有Activity,如果ActivityStack有记录resume或者pause的,移除
for (ActivityRecord record : task.mActivities) {
onActivityRemovedFromStack(record);
}
final int taskNdx = mTaskHistory.indexOf(task);
final int topTaskNdx = mTaskHistory.size() - 1;
// 如果移除的这个taskrecord是当前前台类型,要把他后一个替补为前台类型
if (task.isOverHomeStack() && taskNdx < topTaskNdx) {
final TaskRecord nextTask = mTaskHistory.get(taskNdx + 1);
if (!nextTask.isOverHomeStack() && !nextTask.isOverAssistantStack()) {
nextTask.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
}
}
mTaskHistory.remove(task);// 移除这个task
// 从mLRUActivities移除这个task的所有activity
removeActivitiesFromLRUListLocked(task);
updateTaskMovement(task, true); // 更新这个task被移动后的时间
..........................
task.setStack(null); // 当前移除的这个task现在他的ActivityStack是null
// Notify if a task from the pinned stack is being removed (or moved depending on the mode)
if (mStackId == PINNED_STACK_ID) {
mService.mTaskChangeNotificationController.notifyActivityUnpinned();
}
}
```
因为要从ActivityStack中移除这个taskRecord,而且ActivityStack会记录他里面所有taskRecord中当前resume和pause的Activity,所以这些Activity如果是当前要移除的这个ActivityStack里面的,那就要先置空:
```java
void onActivityRemovedFromStack(ActivityRecord r) {
if (mResumedActivity == r) {
mResumedActivity = null;
}
if (mPausingActivity == r) {
mPausingActivity = null;
}
removeTimeoutsForActivityLocked(r);
}
```
接着,如果当前这个taskRecord是桌面的任务栈,那么移除他后,要把他后面那个给替补上。经过这些处理后,就可以从历史任务栈集合中移除了。最后这个taskRecord的ActivityStack置空。
移除的方法看完了,我们再回到reparent中看下,添加到新ActivityStack方法:
```java
// 添加task到ActivityStack
void addTask(final TaskRecord task, int position, boolean schedulePictureInPictureModeChange,
String reason) {
// TODO: Is this remove really needed? Need to look into the call path for the other addTask
mTaskHistory.remove(task);
position = getAdjustedPositionForTask(task, position, null /* starting */);
final boolean toTop = position >= mTaskHistory.size();
final ActivityStack prevStack = preAddTask(task, reason, toTop);
//把task加入到mTaskHistory集合
mTaskHistory.add(position, task);
//把当前ActivityStack设置到task中
task.setStack(this);
if (toTop) {
updateTaskReturnToForTopInsertion(task);
}
..........
}
```
首先确保下当前ActivityStack中没有taskRecord,然后通过getAdjustedPositionForTask方法再计算一次插入的位置,把taskRecord插入到新ActivityStack的这个位置,再把新ActivityStack设置给这个taskRecord。如果这个taskRecord是目前ActivityStack的顶部,还要调用updateTaskReturnToForTopInsertion方法更新下他的类型。到这里添加也就完成了。
我们回到setTargetStackAndMoveToFrontIfNeeded方法,如果计算出的ActivityStack和复用的ActivityStack的displayId不同,即他们是在不同的屏幕中,那么也和上面的一样,调用reparent方法把taskRecord移动到计算出的ActivityStack中。这个就不多叙述了。这个方法到这里就差不多了,主要是处理ActivityStack。
接着在回到startActivityUnchecked方法,下面会调用这段代码:
```java
if ((mStartFlags & START_FLAG_ONLY_ IF_NEEDED) != 0) {
resumeTargetStackIfNeeded();
return START_RETURN_INTENT_TO_CALLER;
}
```
这个表示如果调用者和要启动的Activity是同一个的话,那么会调用resumeTargetStackIfNeeded方法,我们看下这个方法:
```java
// resume栈焦点的activity
private void resumeTargetStackIfNeeded() {
if (mDoResume) {
mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, null, mOptions);
} else {
ActivityOptions.abort(mOptions);
}
mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
}
```
这里会继续调用resumeFocusedStackTopActivityLocked这个方法:
```java
// 先判断下当前计算出的目标ActivityStack(即targetStack)是不是聚焦stack,是的话调用resumeTopActivityUncheckedLocked
// 不是的话,获取当前焦点stack的顶部Activity,
boolean resumeFocusedStackTopActivityLocked(
ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
// 目标任务栈如果存在,且是焦点
if (targetStack != null && isFocusedStack(targetStack)) {
// 启动目标Activity
return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
// 如果目标栈不存在,或者这个stack不是聚焦的。获取当前聚焦任务栈最上面一个活动的Activity
// 貌似比如横竖屏,会走这里。
final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
if (r == null || r.state != RESUMED) {
mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
} else if (r.state == RESUMED) {
// Kick off any lingering app transitions form the MoveTaskToFront operation.
mFocusedStack.executeAppTransition(targetOptions);
}
return false;
```
这个方法开始判断如果传入的参数是焦点ActivityStack的话,会调用resumeTopActivityUncheckedLocked这个方法,这个方法会调用onresume的生命周期,我们也放到后面统一再讲。
再回到startActivityUnchecked方法,我们接着看下面一个方法setTaskFromIntentActivity,这个方法会根据启动模式,把之前没处理的一些情况再处理一下,之前没处理的方法可能会涉及到对taskRecord的一些调整,具体我们看下这个方法:
```java
// 继续处理不同flag和启动模式下的复用task
private void setTaskFromIntentActivity(ActivityRecord intentActivity) {
// 如果启动flag是要清task的
if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
final TaskRecord task = intentActivity.getTask(); // 获取启动的任务栈
task.performClearTaskLocked(); // 清栈
mReuseTask = task; // 复用栈为task
mReuseTask.setIntent(mStartActivity); // 复用栈的intent是目标Activity
mMovedOtherTask = true;
} else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| mLaunchSingleInstance || mLaunchSingleTask) {
// 进入这里是clear_top,single_task,single_instace,这3种都会把找到相同Activity的上面所有元素清掉
ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity,
mLaunchFlags);
// standard+new_task+clear_top类似于single_task,但会把包含一样的activity也清掉
// 所以会走到这里
if (top == null) {
mAddingToTask = true; // 上面所述的standard的模式最终还是会把要启动的activity加入到这个栈
mStartActivity.setTask(null); // 会用这个task来装activity,所以先清掉之前的task
mSourceRecord = intentActivity; // 还是会复用这个任务栈,所以把调用者换成这个栈的activity
final TaskRecord task = mSourceRecord.getTask(); // 获取复用task
// 如果这个复用task没有ActivityStack
if (task != null && task.getStack() == null) {
mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
null /* bounds */, mLaunchFlags, mOptions);
mTargetStack.addTask(task,
!mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
}
}
// 进入这个分支只会是single_top和standard
} else if (mStartActivity.realActivity.equals(intentActivity.getTask().realActivity)) {
// 如果目标Activity和任务栈的root activity是一个component
// 要启动的Activity和复用任务栈顶Activity是一个component,而且是single_top,调用onNewIntent
// intentActivity此时代表复用任务栈的栈顶Activity,不一定是目标Activity,所以下面判断下是不是一样
if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop)
&& intentActivity.realActivity.equals(mStartActivity.realActivity)) {
ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity,
intentActivity.getTask());
if (intentActivity.frontOfTask) { // 如果这个相同的Activity是root activity,更新task的intent
intentActivity.getTask().setIntent(mStartActivity);
}
// 调用onNewIntent
intentActivity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
mStartActivity.launchedFromPackage);
} else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) {
// 这里可以看上面注释。到这里说明是同一个component,但是是不同intent,所以会把新的Activity加入这个栈,即
// intentActivity所在的栈,所以下面把intentActivity赋值给mSourceRecord,后面会从mSourceRecord取出任务栈加入
mAddingToTask = true;
mSourceRecord = intentActivity;
}
} else if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) { // 多数正常往当前任务栈中添加一个新的activity会走这里
mAddingToTask = true;
mSourceRecord = intentActivity;
} else if (!intentActivity.getTask().rootWasReset) {
intentActivity.getTask().setIntent(mStartActivity);
}
}
```
这个方法可以看到,有好几个if else的分支,我们一个一个来看。第一个FLAG_ACTIVITY_CLEAR_TASK这个我们前面也遇到过了,在上面setTargetStackAndMoveToFrontIfNeeded方法中,我们处理ActivityStack的时候,是跳过这种情况的,所以现在我们来处理这种情况。
对于FLAG_ACTIVITY_CLEAR_TASK肯定是要清空taskRecord的,所以这里就会对taskRecord进行一些处理,处理也比较简单先是获取复用的taskRecord,然后执行performClearTaskLocked方法来清栈,我们看下performClearTaskLocked方法:
```java
// 清理这个任务栈
final void performClearTaskLocked() {
mReuseTask = true;
performClearTaskAtIndexLocked(0, !PAUSE_IMMEDIATELY);
mReuseTask = false;
}
```
继续调用performClearTaskAtIndexLocked方法:
```java
// 清除所有该taskReocrd中Activity
final void performClearTaskAtIndexLocked(int activityNdx, boolean pauseImmediately) {
int numActivities = mActivities.size();
// 遍历ActivityRecord集合
for ( ; activityNdx < numActivities; ++activityNdx) {
// 取出每个ActivityRecord
final ActivityRecord r = mActivities.get(activityNdx);
if (r.finishing) {
continue;
}
// 如果对应的ActivityStack为空
if (mStack == null) {
// Task was restored from persistent storage.
// 释放引用,从ActivityRecord集合中移除
r.takeFromHistory();
mActivities.remove(activityNdx);
--activityNdx;
--numActivities;
// 如果ActivityStack不空,finishActivity
} else if (mStack.finishActivityLocked(r, Activity.RESULT_CANCELED, null,
"clear-task-index", false, pauseImmediately)) {
--activityNdx;
--numActivities;
}
}
}
```
这个方法主要是遍历这个taskRecord中的所有Activity,跳过正在finish的Activity,如果他所在的ActivityStack为空,那么就直接从他的Activity集合中移除就可以了,如果不为空,说明这个Activity可能是正常在运行着的,那么通过finishActivityLocked来finish他,finishActivityLocked这个是正常的Activity的销毁流程方法,现在也暂时不展开讲,后面讲到这几个涉及Activity生命周期方法的地方后再一起来讲。
回到setTaskFromIntentActivity方法,清空任务栈后,把这个任务栈赋值给mReuseTask变量,之前这个变量可能是调用者指定的值,也可能是空,现在确定使用这个找到的复用任务栈,然后把这个任务栈的intent修改为要启动的Intent。
接下来看第二个分支,这个分支之前我们特地处理过,是FLAG_ACTIVITY_CLEAR_TOP模式或者single_instance或者single_task这三种模式的情况,大家可以往前面翻一下看看,这里和前面一样是调用了performClearTaskLocked这个方法在任务栈中寻找是否有相同的Activity,如果有的话会把上面的Activity都清掉(除了有种特殊情况standard+new_task+clear_top这种模式的),然后返回找到的Activity,也就是这个的top变量。前面的处理是top变量为非空的时候就直接调用onNewIntent方法来回调Activity的生命周期函数了,大家不记得的可以往前面看下。但是如果返回是空的时候没做什么特殊的处理,这里就开始处理返回为空的情况了。
由于我们已经找到一个复用的taskRecord,虽然已经把里面有关Activity可能已经清空了,但是还是会把新的Activity放入这里,所以先把之前启动Activity的task置空,然后把复用Activity赋值给调用者mSourceRecord,这里根据google注释的意思是,因为会使用复用的任务栈,所以就假装调用者就是这个任务栈找出的Activity了,这个在后面会看到通过mSourceRecord来获取任务栈。然后获取这个复用的taskRecord,此时依然要判断下这个taskRecord有没有ActivityStack,如果没有的话通过computeStackFocus获取,最后把这个taskRecord设置进ActivityStack。这里computeStackFocus方法计算ActivityStack,里面调用了之前讲过了getLaunchStack来获取一个合适的ActivityStack,我们看下这个方法:
```java
// 获取一个要加入的可用的ActivityStack
private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds,
int launchFlags, ActivityOptions aOptions) {
// 获取这个Activity的TaskRecord
final TaskRecord task = r.getTask();
// 获取启动的ActivityStack
ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions);
if (stack != null) {
return stack;
}
// 如果上面启动ActivityStack没有,说明无法设置一个新的焦点ActivityStack,那么取出目标这个Activty所在的ActivityStack,非空返回
final ActivityStack currentStack = task != null ? task.getStack() : null;
if (currentStack != null) {
if (mSupervisor.mFocusedStack != currentStack) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
"computeStackFocus: Setting " + "focused stack to r=" + r
+ " task=" + task);
} else {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
"computeStackFocus: Focused stack already="
+ mSupervisor.mFocusedStack);
}
return currentStack;
}
// 如果目标activty有container,取出这个container的ActivityStack
final ActivityStackSupervisor.ActivityContainer container = r.mInitialActivityContainer;
if (container != null) {
// The first time put it on the desired stack, after this put on task stack.
r.mInitialActivityContainer = null;
return container.mStack;
}
// 如果目标activity可以显示在当前焦点ActivityStack中,则返回当前焦点ActivityStack
if (canLaunchIntoFocusedStack(r, newTask)) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
"computeStackFocus: Have a focused stack=" + mSupervisor.mFocusedStack);
return mSupervisor.mFocusedStack;
}
// 根据调用者displayId获取ActivityStack
if (mSourceDisplayId != DEFAULT_DISPLAY) {
// Try to put the activity in a stack on a secondary display.
stack = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r);
if (stack == null) {
// If source display is not suitable - look for topmost valid stack in the system.
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
"computeStackFocus: Can't launch on mSourceDisplayId=" + mSourceDisplayId
+ ", looking on all displays.");
stack = mSupervisor.getNextValidLaunchStackLocked(r, mSourceDisplayId);
}
}
// 如果调用者displayId不能用,那么再取主屏的ActivityStack。如果还是不行的话,取其他类型的ActivityStack
if (stack == null) {
// We first try to put the task in the first dynamic stack on home display.
final ArrayList<ActivityStack> homeDisplayStacks = mSupervisor.mHomeStack.mStacks;
for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
stack = homeDisplayStacks.get(stackNdx);
if (isDynamicStack(stack.mStackId)) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
"computeStackFocus: Setting focused stack=" + stack);
return stack;
}
}
// If there is no suitable dynamic stack then we figure out which static stack to use.
final int stackId = task != null ? task.getLaunchStackId() :
bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
FULLSCREEN_WORKSPACE_STACK_ID;
stack = mSupervisor.getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
}
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
+ r + " stackId=" + stack.mStackId);
return stack;
}
```
这个方法首先还是通过getLaunchStack这个方法来计算ActivityStack,如果非空的话就返回。否则通过其他方法在获取,比如复用taskRecord所在的ActivityStack,初始化这个Activity是否传入的ActivityStack,或者是当前的焦点栈,或者新创建一个ActivityStack等等,会通过各种方法来寻找,看看能不能获得一个可用的Activity,这么具体方法感兴趣的同学可以在看下具体方法。我们再回到setTaskFromIntentActivity方法。
前面说了两种情况,现在我们再来看第三种情况,要启动的Activity和复用栈的ComponentName是一样的,这里首先说了,起码是可以把这个要启动的Activity加入到这个taskRecord中。同时如果复用Activity和启动的Activity的ComponentName也是一样的,由于前面我们寻找复用栈的时候,返回的Activity都是取的栈顶元素,所以这里就表明,当前栈顶元素就是要启动的Activity,并且启动模式是single_top,那么直接调用deliverNewIntentLocked触发Activity的onNewIntent就可以了,这个应该还是很好理解的。如果和栈顶元素不一样,说明需要创建一个Activity加入,那么这里句先把mAddingToTask置为true,说明已经找到了一个要加入的taskRecord,然后把mSourceRecord = intentActivity,这样调用者就变成了复用栈的Activity,后面会从这个调用者里面取出taskRecord。
上面是几种特殊的情况需要再处理一下,后面就是正常的创建一个Activity加入复用任务栈就可以了,这里就不多叙述了。我们返回startActivityUnchecked方法,如果当前虽然有找到复用的任务栈,但是最终这个复用任务栈不是个合适的任务栈,就会直接返回当前的前台界面,代码如下:
```java
if (!mAddingToTask && mReuseTask == 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.
resumeTargetStackIfNeeded();
if (outActivity != null && outActivity.length > 0) {
outActivity[0] = reusedActivity;
}
return START_TASK_TO_FRONT;
}
```
这里mAddingToTask为false同时mReuseTask为null才会执行,表示最终没有找到一个合适的任务栈,那么就简单的调用resumeTargetStackIfNeeded,把当前前台显示就可以了。
到这里我们都在讨论找到复用任务栈的情况,到这里也就都处理完了,接下会根据找到的taskRecord,ActivityStack继续启动Activity。这个方法下一篇文章再说,这篇文章内容已经挺多的了,由于这篇文章总体来说在一个方法里面执行,本来也不想画时序图了,但是还是画一下,不要坏了规矩!!

最后复用任务栈里面涉及的方法比较多也比较杂,这个图里也不一一列出了,代码的调用的入口比较统一,相信看过文章的同学应该不看找到,这里时序图中就用一个中文说明表示了。
AMS源码分析(二)