ActivityStarter封装了一个activity启动的过程(包括上下文环境),不采用情景栈分析法真的很难分析,这里采用情景分析法把该函数分析. 整体代码如下:
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
//1. 初始化环境和lunchModeFlags
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor);
computeLaunchingTaskFlags();
computeSourceStack();
mIntent.setFlags(mLaunchFlags);
//2. 复用activity逻辑
mReusedActivity = getReusableIntentActivity();
......
if (mReusedActivity != null) {
......
mReusedActivity = setTargetStackAndMoveToFrontIfNeeded(mReusedActivity);
......
setTaskFromIntentActivity(mReusedActivity);
if (!mAddingToTask && mReuseTask == null) {
resumeTargetStackIfNeeded();
return START_TASK_TO_FRONT;
}
}
.......
//singleTop 或者singleInstance的处理
if (dontStart) {
ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.task);
// For paranoia, make sure we have correctly resumed the top activity.
if (mDoResume) {
mSupervisor.resumeFocusedStackTopActivityLocked();
}
......
top.deliverNewIntentLocked(
mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
......
return START_DELIVERED_TO_TOP;
}
//3. 设置对应task并带到前台
if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
newTask = true;
setTaskFromReuseOrCreateNewTask(taskToAffiliate);
......
} else if (mSourceRecord != null) {
......
final int result = setTaskFromSourceRecord();
......
} else if (mInTask != null) {
......
final int result = setTaskFromInTask();
if (result != START_SUCCESS) {
return result;
}
} else {
setTaskToCurrentTopOrCreateNewTask();
}
// 4. 启动Activity
mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);
// 5. 使Activity可见
if (mDoResume) {
if (!mLaunchTaskBehind) {
mService.setFocusedActivityLocked(mStartActivity, "startedActivity");
}
final ActivityRecord topTaskActivity = mStartActivity.task.topRunningActivityLocked();
if (!mTargetStack.isFocusable()
|| (topTaskActivity != null && topTaskActivity.mTaskOverlay
&& mStartActivity != topTaskActivity)) {
.... . .
mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
mWindowManager.executeAppTransition();
} else {
mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
mOptions);
}
} else {
mTargetStack.addRecentActivityLocked(mStartActivity);
}
......
return START_SUCCESS;
}
我们先整体介绍下这里activity启动的过程
- 计算启动的flags,首先计算flags是因为后面要根据flags选择启动的task,所以这一步是铺垫作用.这里读者可能会问为什么要计算,因为有些flags是冲突的,所以需要计算一下.
- 对于找到task的过程其实还包含一些特殊的activity模式和标志的处理,以及对activity复用的逻辑.可以复用的情况包含如下几种
- LaunchSingleInstance
- LaunchSingleTask
- (singleTop|FLAG_ACTIVITY_SINGLE_TOP) && activity已经在已经在栈顶. 对于在栈顶这一条,如果指定了FLAG_ACTIVITY_NEW_TASK标志或者要启动的activity模式为LaunchSingleInstance或者 LaunchSingleTask的activity,又或者mOptions.getLaunchTaskId()指定了taskId(这个优先级要高于参数中指定的inTask).其实可以先把其他的符合复用条件的task放到栈顶.所以在复用之前还有根据条件将复用的task带到前台的环节. 对于activity复用后这些activity是不用启动的,之需要使他们可见可以返回了,还第5步有着类似的逻辑
- 计算在哪个task中启动,这里可定是有四种种情况了
- 新创建一个task启动 (指定了FLAG_ACTIVITY_NEW_TASK标志,没有找到复用的task)
- 在sourceRecord(也就是调用startActivity的activity),没有指定FLAG_ACTIVITY_NEW_TASK标志,并且sourceRecord不为空
- 在指定的task中启动, 一般是用于恢复task
- 在当前焦点stack的topTask中启动 , 既没有FLAG_ACTIVITY_NEW_TASK,又没有指定task并且还没有mSourceRecord,这种情况根据注释说不会发生
- 在锚定task并把它带到前台之后就可以启动activity了,使用 mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions)
- mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
mOptions) 使activity可见
下面就围绕这4步对代码进行展开说明
一.对启动参数进行初始化,主要是下面三个函数
- setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor);
computeLaunchingTaskFlags();
computeSourceStack();
mIntent.setFlags(mLaunchFlags);
private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
boolean doResume, int startFlags, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
setInitialState 函数比较简单,主要初始化了如下变量
- mStartActivity 要启动的Activity的ActivityRecord.
- mIntent 调用者传递的intent
- mOptions 调用者传递的额外参数,主要用于动画和task,stack,display等额外信息的传递
- mCallingUid 调用者uid
- mSourceRecord 调用者activity
- mLaunchBounds 打掉主要给PIP模式使用,指定activity的大小
- mLaunchSingleTop 要启动的activity是否为singleTop模式的activity
- mLaunchSingleInstance: singleInstance模式的activity
- mLaunchSingleTask : singleTask模式的activity
- mLaunchFlags : 这个值通过adjustLaunchFlagsToDocumentMode计算,主要是通过intent中传递的flags与在AndroidManifest.xml中配置的lunchMode和documentLaunchMode做一些处理,并把DocumentMode转化为lunchModeFlags,操作如下:
private int adjustLaunchFlagsToDocumentMode(ActivityRecord r, boolean launchSingleInstance,
boolean launchSingleTask, int launchFlags) {
if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
(launchSingleInstance || launchSingleTask)) {
launchFlags &=
~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK);
} else {
switch (r.info.documentLaunchMode) {
case ActivityInfo.DOCUMENT_LAUNCH_NONE:
break;
case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
break;
case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
break;
case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
launchFlags &= ~FLAG_ACTIVITY_MULTIPLE_TASK;
break;
}
}
return launchFlags;
}
可以看到launchSingleInstance和launchSingleTask模式的activity是不支持FLAG_ACTIVITY_NEW_DOCUMENT标志的,不允许.所以直接干掉这个表示,使之失效.
另外对于指定了 documentLaunchMode的activity
- DOCUMENT_LAUNCH_NONE不用任何操作.
- DOCUMENT_LAUNCH_INTO_EXISTING或者DOCUMENT_LAUNCH_ALWAYS 代表activity自动带有FLAG_ACTIVITY_NEW_DOCUMENT标志
- DOCUMENT_LAUNCH_NEVER 则会把FLAG_ACTIVITY_MULTIPLE_TASK标志去掉,表示activity永远不主动在新的task中创建
所以这里我们可以知道对于document模式,都会转化为FLAG_ACTIVITY_MULTIPLE_TASK和FLAG_ACTIVITY_NEW_DOCUMENT标志进行处理
- mLaunchTaskBehind 不把这个activity启动到前台,是通过Activity的启动参数ActivityOptions传递的,但是以document模式启动的activity才会处理这个参数,所以还要看是否设置了这个标志.
mLaunchTaskBehind = r.mLaunchTaskBehind
&& !mLaunchSingleTask && !mLaunchSingleInstance
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
代码是这么写的,其实判断mLaunchSingleTask && !mLaunchSingleInstance是没有必要的,因为在前面adjustLaunchFlagsToDocumentMode里面已经处理了.
- 后面这段逻辑都是对于mLaunchFlags的处理 后面
首先sendNewTaskResultRequestIfNeeded()函数,如果启动参数带有FLAG_ACTIVITY_NEW_TASK,则要启动一个新的task,这种情况有可能栈顶的task会被切到后台,所以发送给它一个RESULT_CANCELED的结果.所以要获得一个结果请别指定FLAG_ACTIVITY_NEW_TASK
然后对于指定了FLAG_ACTIVITY_NEW_DOCUMENT启动参数的情况,并且r.resultTo也就是说不接收启动结果的情况添加FLAG_ACTIVITY_NEW_TASK标志,也就是说FLAG_ACTIVITY_NEW_DOCUMENT默认是等效于FLAG_ACTIVITY_NEW_TASK的,只是启动的时候要求接受结果还是以接受结果为准,不把它启动到一个新的task上面.
经过上面两个步骤对FLAG_ACTIVITY_NEW_TASK的处理,如果documentLaunchMode == DOCUMENT_LAUNCH_ALWAYS或者mLaunchTaskBehind满足的情况下并且设置了FLAG_ACTIVITY_NEW_TASK则要给mLaunchFlags添加FLAG_ACTIVITY_MULTIPLE_TASK标志,为什么这么做呢,因为FLAG_ACTIVITY_MULTIPLE_TASK和FLAG_ACTIVITY_NEW_TASK会产生化学反映,尽量(这里说尽量是由于singleTask和singleInstance优先级要高于FLAG_ACTIVITY_MULTIPLE_TASK)把要启动的activity放到新的的task里面,首先mLaunchTaskBehind为真的情况下要启动的activity不会被启动到前台,但是在多任务下观察要求它在一个单独的放在调用者task的前面,所以它的语义其实就是启动到一个新的task里面. 对于r.info.documentLaunchMode == DOCUMENT_LAUNCH_ALWAYS的情况,google source上也有说明是默认添加FLAG_ACTIVITY_MULTIPLE_TASK将docactivity启动到新的task里面. - mSupervisor.mUserLeaving 变量表示当用户手动把activity切换出去的时候回调Activity->onUserLeaveHint() 方法,这里如果启动activity指定了FLAG_ACTIVITY_NO_USER_ACTION标志,就不会回调这个函数
- mDoResume 表示否是要执行resume函数,使activity可见,有些情况是不需要可见的,比如这个要启动的activity不在栈顶. 而且这里还会判断是否要延迟启动,比如activity所在的user还没有启动起来,就会设置ActivityRecord.delayedResume = true. 对于参数ActivityOptions指定了lunchTaskid和明确要求覆盖原有task的情况,要找到指定的task,但是如果task最上面的activity是不可见的,就不需要使要启动的activity显示,并且明确不要移动task到前台.所以可以看到TaskOverlay只是为了把activity放到对应的task上面,并不是为了把这个activity打到用户脸上
- mNotTop:表示不把当前栈顶的activity作为栈顶task,这是怎么个情况呢,比如说ChooseActivity,用于选择启动哪个应用的,ChooseActivity是由系统启动的,做操作的时候不需要考虑到它,所以这里会设置这个标志.表示做一些操作的时候不考虑topActivity, 那么topActivity是谁呢,就是我们要启动的activity.
- mInTask 为调用参数中指定的task,后面还对task做了些校验,如果这个task不在inRecents(因为我们选择task的时候是在Recents中的,但是执行到这里之后可能已经不在了),指定一个不太Recents中的task是无用的,所以对于不在Recents里的inTask要设置它为空
- mStartFlags 多数参数用于启动activity的调试,其中START_FLAG_ONLY_IF_NEEDED标志的作用是检查自己是否在前台,如果在前台则不需要启动,不在才去启动,它的行为类似于Intent#FLAG_ACTIVITY_SINGLE_TOP flags或者
singleTask 或者 singleTop 但是不执行onNewIntent. 所以这个flags只是为了检查自己是否在前台,所以必须调用者和要启动的activity具有相同的ComponentName,否则标志无效 - mNoAnimation 为true表示切换的过程不执行activity切换动画
2 private void computeLaunchingTaskFlags() 计算mLaunchFlags,这里进一步计算关于task的启动参数.
rivate void computeLaunchingTaskFlags() {
if (mSourceRecord == null && mInTask != null && mInTask.getStack() != null) {
//1 对于mInTask的处理
final Intent baseIntent = mInTask.getBaseIntent();
final ActivityRecord root = mInTask.getRootActivity();
if (baseIntent == null) {
ActivityOptions.abort(mOptions);
throw new IllegalArgumentException("Launching into task without base intent: "
+ mInTask);
}
if (mLaunchSingleInstance || mLaunchSingleTask) { //对于这两种类型的activity要想在新的task中创建(新的rootActivity)必须task 中不包含才能创建新的
......
}
if (root == null) { //注意mInTask默认的语义就是把要启动的activity作为task的rootActivity
final int flagsOfInterest = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK
| FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS;
mLaunchFlags = (mLaunchFlags & ~flagsOfInterest)
| (baseIntent.getFlags() & flagsOfInterest);
mIntent.setFlags(mLaunchFlags);
mInTask.setIntent(mStartActivity);
mAddingToTask = true;
} else if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
mAddingToTask = false;
} else {
mAddingToTask = true;
}
mReuseTask = mInTask;
} else {
// 2 sourceRecord的处理
mInTask = null;
if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null && mSourceRecord.isFreeform()) {
mAddingToTask = true;
}
}
//3 最后针对需要添加FLAG_ACTIVITY_NEW_TASK的情况处理
if (mInTask == null) {
if (mSourceRecord == null) {
if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
} else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
} else if (mLaunchSingleInstance || mLaunchSingleTask) {
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
}
}
代码也是分为三个部分处理. 1,2 是两种条件的处理,3是总的处理.
首先对于条件1进行说明,mSourceRecord == null && mInTask != null && mInTask.getStack() != null 这个条件主要是用于处理mInTask, 第一个条件mSourceRecord == null说明使用SourceRecord确定task的优先级要高于参数中指定的mInTask. 如果SourceRecord的task好指定的mInTask都存在,则以mSourceRecord的为准.
- 1 mInTask的情况,如果要启动的activity是一个singleInstance或者singleTop的activity,因为设置了这种模式的activity只能在一个task中启动,所以要判断指定的task是不是就是他们现在所在的task,如果不是的话则证明指定的task是有问题的,就会抛出异常. 对singleInstance或者singleTop处理之后,说明这个task是可以复用的了,所以首先如果这个task是一个空的task,里面还没有activity,可以把我们要启动的activity作为它的rootTask启动,所以会对这个task做初始化操作. 并且设置变量mAddingToTask,表示明确需要把这个activity放到一个task中去. 如果这个task不是一个空的task,而设置了FLAG_ACTIVITY_NEW_TASK的启动参数,那么只是把这个task带到前台,不去添加新的activity. 这部分我们要参考systemui的实现才能说的清楚,总之不添加要启动的activity到这个task,设置 mAddingToTask = false. 最后一种情况是不满足上述情况的情况,也就是说不是一个空的task,并且也没有设置FLAG_ACTIVITY_NEW_TASK启动参数,所以需要添加一个activity到这个task中,设置 mAddingToTask = true. 如果三种条件能执行完说明用户指定的task是可用的,设置mReuseTask = mInTask
- 2.对于条件2 满足的情况,说明sourceRecord不为空或者用户没有指定mInTask,这种情况就需要设置mInTask 为null,因为sourceRecord优先级大于mInTask. 这个条件还对特殊情况做了处理,保证要启动的activity尽量放到SourceRecord 之上.
- 3 对于上述条件判断完成,如果mInTask为空,也就是还没有明确task,就需要对task相关的参数做一些修正. 包括三个条件
首先sourceRecord为空的情况,无法附加要启动的activity到sourceRecord的task中,所以mLaunchFlags 需要添加FLAG_ACTIVITY_NEW_TASK参数,在新的task上启动activity
如果sourceRecord存在但是lunchMode为singleInstance的,这种activity只能自己独自在一个task上,所以mLaunchFlags也要添加FLAG_ACTIVITY_NEW_TASK参数,在新的task上启动activity
最后lunchMode为SingleInstance 或者SingleTask的activity自带FLAG_ACTIVITY_NEW_TASK标志,尽量在原有的task上启动activity,所以mLaunchFlags 需要添加FLAG_ACTIVITY_NEW_TASK参数,在新的task上启动activity
注意!!! :新的task并非指一个新创建的task,它的语义代表尽量不在SourceRecord的task上启动,说尽量是由于可能使用FLAG_ACTIVITY_NEW_TASK找到的task就是sourceRecord所在的task
3 private void computeSourceStack() 函数用于计算sourceStack, 就是调用者所在的stack
private void computeSourceStack() {
if (mSourceRecord == null) {
mSourceStack = null;
return;
}
if (!mSourceRecord.finishing) {
mSourceStack = mSourceRecord.getStack();
return;
}
if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0) {
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
mNewTaskInfo = mSourceRecord.info;
final TaskRecord sourceTask = mSourceRecord.getTask();
mNewTaskIntent = sourceTask != null ? sourceTask.intent : null;
}
mSourceRecord = null;
mSourceStack = null;
}
代码很简单,首先如果不存在mSourceRecord那么mSourceStack也是不存在的
如果mSourceRecord所代表的activity没有被关闭则直接使用sourceRecord所代表的Activity作为sourceStack
最后的情况则代表mSouorceRecord所代表的activity已经执行的finish方法,那么好了,我们要强行添加FLAG_ACTIVITY_NEW_TASK标志使activity启动到一个新的task中. 并且将原来sourceRecord的信息保存下来主要就是原来的taskinfo信息和intent信息, 最后设置mSourceRecord = null;
mSourceStack = null;保存task的intent信息和taskinfo信息是为了新建task的时候尝试恢复这个task
二 复用activity的情况
1 getReusableIntentActivity()函数,函数的名字很不友好,看似是获取复用的activity实则是获取复用的task
private ActivityRecord getReusableIntentActivity() {
boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
(mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| mLaunchSingleInstance || mLaunchSingleTask;
putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
ActivityRecord intentActivity = null;
if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
intentActivity = task != null ? task.getTopActivity() : null;
} else if (putIntoExistingTask) {
if (mLaunchSingleInstance) {
intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info, false);
} else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
!mLaunchSingleTask);
} else {
intentActivity = mSupervisor.findTaskLocked(mStartActivity, mSourceDisplayId);
}
}
return intentActivity;
}
这里罗列了一些可以复用task的情况,主要包括设置了FLAG_ACTIVITY_NEW_TASK标志并且没有设置FLAG_ACTIVITY_MULTIPLE_TASK的情况,如果设置了FLAG_ACTIVITY_MULTIPLE_TASK标志需要另外起一个task盛放要启动的activity. 以及lunchMode为singleTask或者singleInstance的情况和明确在AppOptions中指定的使用的task的情况。 对于明确指定的情况肯定要复用task,前两种情况呢,还要在不使用inTask的情况和mStartActivity.resultTo不为空的情况。 指定了InTask参数和mStartActivity.resultTo不为空的情况都会单独处理。
还要说明的是对于singleInstance的情况要找到task中包含要启动activity的task进行复用,寻找到过程使用mSupervisor.findActivityLocked(mIntent, mStartActivity.info, false)函数,其中第三个参数为false表示不需要匹配intent,因为lunchMode为singleInstance的时候,它所在的task只能放一个activity. 如果存在这样的task那就必然使我们要找的task了。对于指定了FLAG_ACTIVITY_LAUNCH_ADJACENT的情况也是要找到与启动activity相同的task。 最后如果并非是singleInstance或者FLAG_ACTIVITY_LAUNCH_ADJACENT方式启动activity,意味着使用mSupervisor.findTaskLocked(mStartActivity, mSourceDisplayId),这种情况只是找到合适的task,并不要求task中包含对应的activity。
- 获取到可复用的task和这个task上的activity(或许是与我们要启动的activity相同compoentname的activity或是可复用task栈顶的activity) ,整体流程如下
if (reusedActivity != null) {
......
if (mStartActivity.getTask() == null) {
mStartActivity.setTask(reusedActivity.getTask());
}
if (reusedActivity.getTask().intent == null) {
reusedActivity.getTask().setIntent(mStartActivity);
}
//清除task中复用的activity上面的activity
if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isDocumentLaunchesIntoExisting(mLaunchFlags)
|| mLaunchSingleInstance || mLaunchSingleTask) {
final TaskRecord task = reusedActivity.getTask();
final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
mLaunchFlags);
if (reusedActivity.getTask() == null) {
reusedActivity.setTask(task);
}
if (top != null) {
if (top.frontOfTask) {
top.getTask().setIntent(mStartActivity);
}
//执行newIntent
ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask());
top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
mStartActivity.launchedFromPackage);
}
}
sendPowerHintForLaunchStartIfNeeded(false /* forceSend */);
//将复用的task拿到前台
reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);
final ActivityRecord outResult =
outActivity != null && outActivity.length > 0 ? outActivity[0] : null;
if (outResult != null && (outResult.finishing || outResult.noDisplay)) {
outActivity[0] = reusedActivity;
}
//把task拿到前台就可以了不需要重启
if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
resumeTargetStackIfNeeded();
return START_RETURN_INTENT_TO_CALLER;
}
//根据复用情况设置task
setTaskFromIntentActivity(reusedActivity);
if (!mAddingToTask && mReuseTask == null) {
resumeTargetStackIfNeeded();
if (outActivity != null && outActivity.length > 0) {
outActivity[0] = reusedActivity;
}
return START_TASK_TO_FRONT;
}
}
这段代码还是比较不容易弄懂的,注意我们这里的mStartActivity和reusedActivity的含义,mStartActivity才是我们要启动的activity.,而reusedActivity只不过是我们要复用的task上的一个activity,有可能是我们要复用的activity,也可能只是这个task最上面的activity.
这段代码首先如果要启动的activity还没有设置task,就设置成复用的task,另外如果复用的task还没有mStartActivity则根据activity设置baseintent.
后面一段清除task中复用的activity上面的activity的逻辑,主要是对于FLAG_ACTIVITY_CLEAR_TOP(含义就是清除与mStartActivity相同的activity上面的activity). 另外对于isDocumentLaunchesIntoExisting(mLaunchFlags)
|| mLaunchSingleInstance || mLaunchSingleTask 这三种模式的也需要清除StartActivity相同的activity上面的activity。函数体使用 final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
mLaunchFlags);函数进行task的清除,值得注意的一点是performClearTaskLocked(ActivityRecord newR, int launchFlags)函数内部如果和mStartActivity相同compoentname的activity的启动模式是默认的ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE,则也会将这个activity销毁。对于SingleInstance || SingleTask|| singleTop启动模式的则不会被销毁。所以task.performClearTaskForReuseLocked(mStartActivity,
mLaunchFlags)返回后对于要启动的activity的启动模式为LAUNCH_MULTIPLE的,返回值top肯定是空的。所以不会发送newIntent的请求给客户端。
后面setTargetStackAndMoveToFrontIfNeeded(reusedActivity)函数是把复用的task所在的stack设置为fourceStack并且把复用的task拿到栈顶。 请参考https://blog.csdn.net/woai110120130/article/details/79720840
执行完setTargetStackAndMoveToFrontIfNeeded函数后就可以设置返回结果了,为什么这里就可以设置了呢,注意判断条件
后面对startFlags参数的START_FLAG_ONLY_IF_NEEDED标志处理,这种情况不需要去真的启动activity,只需要使task放到前台就可以了,这种情况多是从桌面点击图标恢复task的情况。
后面 setTaskFromIntentActivity(reusedActivity)也是比较关键的函数,我在代码上进行注释,具体细节就不在讲解,最后对于复用的情况,到这里就处理完了,只是把task带到了前台,所以返回START_TASK_TO_FRONT。
private void setTaskFromIntentActivity(ActivityRecord intentActivity) {
if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) { //条件1 清空task
final TaskRecord task = intentActivity.getTask();
task.performClearTaskLocked();
mReuseTask = task;
mReuseTask.setIntent(mStartActivity);
mMovedOtherTask = true;
} else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| mLaunchSingleInstance || mLaunchSingleTask) { //2 clear top
ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity,
mLaunchFlags);
if (top == null) {
mAddingToTask = true;
mStartActivity.setTask(null);
mSourceRecord = intentActivity;
final TaskRecord task = mSourceRecord.getTask();
if (task != null && task.getStack() == null) {
mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
null /* bounds */, mLaunchFlags, mOptions);
mTargetStack.addTask(task,
!mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
}
}
} else if (mStartActivity.realActivity.equals(intentActivity.getTask().realActivity)) {
//3 singleTop
if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop)
&& intentActivity.realActivity.equals(mStartActivity.realActivity)) {
ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity,
intentActivity.getTask());
if (intentActivity.frontOfTask) {
intentActivity.getTask().setIntent(mStartActivity);
}
intentActivity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
mStartActivity.launchedFromPackage);
} else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) {
mAddingToTask = true;
mSourceRecord = intentActivity;
}
} else if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) { //4 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
mAddingToTask = true;
mSourceRecord = intentActivity;
} else if (!intentActivity.getTask().rootWasReset) { //5 rootWasReset
intentActivity.getTask().setIntent(mStartActivity);
}
}
setTaskFromIntentActivity 从函数的名字来看是根据intentActivity设置task, 参数是ActivityStarter->startActivityUnchecked()中的reusedActivity,函数主要分为五中情况处理
- 1 首先对FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK的处理,这种情况要清除task. 所以后面的处理就是清空task的操作和对task的重新设置
- 2 这个条件其实外面已经处理过,但是这里更侧重的是对条件提top == null的情况做处理(主要是那种非lunchMode为singleInstance 或者singleTask的情况)。 这里会把activity的task设置为null,因为后面会重新计算(不使用NEW_TASK找到的task,但是要尽量放到intentActivity所在的task,所以这里先把targetStack设置为intentActivity所在的stack上)
- 3 第三种情况其实很简单,常见的情况就是从桌面进入一个task的情况,如打开了日历应用,到了创建日程的界面,然后按home键返回,再次点击桌面日历图标,这时候条件3就会满足。 函数体内首先对FLAG_ACTIVITY_SINGLE_TOP和lunchMode为SingleTop,并且task中只有一个activity的情况,那么要发送给这个activity一个newIntent事件更新页面。
另外条件体里面的另一个分支所处理情况为task的baseIntent的intent和启动的activity intent不同的情况,说明并不是从桌面启动的,也不能直接使用这个task,需要新创建一个activity. 注意函数里面很多设置mAddingToTask为真的表示不能复用activity,需要创建新的放到task里面 - 4 条件四是对FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标志出处理 ,这个主要用于快捷图标和或者从通知启动,这种情况需要替换task最上面的activity,所以需要添加activity到task中,设置mSourceRecord为intentActivity方便后面将activity启动到intentActivity所在的task中。
5 所说的情况是希望把要启动的activity放到task的最底部,但是现在的代码结构做不到,只是把task更新为mStartActivity为baseIntent
到这里setTaskFromIntentActivity分析完了,我们来总结下这里设置的几个变量mReuseTask 复用task, 这里有两种情况会设置,第一种是inUseTask可用的时候,第二种情况是FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK情况
mMovedOtherTask 表示task已经被移动到顶端,不需要更新task的returnType
mAddingToTask 表示要新创建activity,不能复用
回到二(Activity复用的情况下,只剩下一段代码)如下
if (!mAddingToTask && mReuseTask == null) {
resumeTargetStackIfNeeded();
if (outActivity != null && outActivity.length > 0) {
outActivity[0] = reusedActivity;
}
return START_TASK_TO_FRONT;
}
这里mAddingToTask为假表示可以复用activity,mReuseTask不为空说明task被清除了,所以这两个条件满足可以复用activity并且task没有被清空就可以设置栈顶activity可见并返回START_TASK_TO_FRONT,到这里activity的复用逻辑就快要执行完成了
跳出reusedActivity不为空的情况,因为获取resuedActivity的时候并没有处理所有情况,如mInTask == null 或者mStartActivity.resultTo == null的情况,这里检查topActivity是否为我们要启动的activity. 代码如下
final ActivityStack topStack = mSupervisor.mFocusedStack;
final ActivityRecord topFocused = topStack.topActivity();
final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
final boolean dontStart = top != null && mStartActivity.resultTo == null
&& top.realActivity.equals(mStartActivity.realActivity)
&& top.userId == mStartActivity.userId
&& top.app != null && top.app.thread != null
&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
|| mLaunchSingleTop || mLaunchSingleTask);
if (dontStart) {
ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.getTask());
// For paranoia, make sure we have correctly resumed the top activity.
topStack.mLastPausedActivity = null;
if (mDoResume) {
mSupervisor.resumeFocusedStackTopActivityLocked();
}
ActivityOptions.abort(mOptions);
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!
return START_RETURN_INTENT_TO_CALLER;
}
top.deliverNewIntentLocked(
mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
// Don't use mStartActivity.task to show the toast. We're not starting a new activity
// but reusing 'top'. Fields in mStartActivity may not be fully initialized.
mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredLaunchStackId,
preferredLaunchDisplayId, topStack.mStackId);
return START_DELIVERED_TO_TOP;
}
主要就是对FLAG_ACTIVITY_SINGLE_TOP和singleTask,singleTop, singleTask的处理,代码很简单就不解释了。
到这里activity的复用逻辑就介绍完了
三 第三部分的操作主要就是task的创建或者复用,并且拿到前台。
if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) { //1创建新的或者重用mReuseTask
newTask = true;
String packageName= mService.mContext.getPackageName();
if (mPerf != null) {
mStartActivity.perfActivityBoostHandler =
mPerf.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, packageName, -1, BoostFramework.Launch.BOOST_V1);
}
result = setTaskFromReuseOrCreateNewTask(
taskToAffiliate, preferredLaunchStackId, topStack);
} else if (mSourceRecord != null) { //2 从mSourceRecord获取task
result = setTaskFromSourceRecord();
} else if (mInTask != null) { //3 使用mInTask
result = setTaskFromInTask();
} else { //4 其他情况
// 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.
setTaskToCurrentTopOrCreateNewTask();
}
代码也是分为四中情况 ,代码注释中写的很清楚,函数的名字也比较清晰,就不分析了,我们在分析stack管理的时候再进行分析。
四. 最后这段代码主要的逻辑就是activity的情动和显示的逻辑,也很简单
mService.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName,
mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
mService.grantEphemeralAccessLocked(mStartActivity.userId, mIntent,
mStartActivity.appInfo.uid, UserHandle.getAppId(mCallingUid));
if (mSourceRecord != null) {
mStartActivity.getTask().setTaskToReturnTo(mSourceRecord);
}
if (newTask) {
EventLog.writeEvent(
EventLogTags.AM_CREATE_TASK, mStartActivity.userId,
mStartActivity.getTask().taskId);
}
ActivityStack.logStartActivity(
EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask());
mTargetStack.mLastPausedActivity = null;
sendPowerHintForLaunchStartIfNeeded(false /* forceSend */);
mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
mOptions);
if (mDoResume) {
final ActivityRecord topTaskActivity =
mStartActivity.getTask().topRunningActivityLocked();
if (!mTargetStack.isFocusable()
|| (topTaskActivity != null && topTaskActivity.mTaskOverlay
&& mStartActivity != topTaskActivity)) {
// If the activity is not focusable, we can't resume it, but still would like to
// make sure it becomes visible as it starts (this will also trigger entry
// animation). An example of this are PIP activities.
// Also, we don't want to resume activities in a task that currently has an overlay
// as the starting activity just needs to be in the visible paused state until the
// over is removed.
mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
// Go ahead and tell window manager to execute app transition for this activity
// since the app transition will not be triggered through the resume channel.
mWindowManager.executeAppTransition();
} else {
// If the target stack was not previously focusable (previous top running activity
// on that stack was not visible) then any prior calls to move the stack to the
// will not update the focused stack. If starting the new activity now allows the
// task stack to be focusable, then ensure that we now update the focused stack
// accordingly.
if (mTargetStack.isFocusable() && !mSupervisor.isFocusedStack(mTargetStack)) {
mTargetStack.moveToFront("startActivityUnchecked");
}
mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
mOptions);
}
} else {
mTargetStack.addRecentActivityLocked(mStartActivity);
}
mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredLaunchStackId,
preferredLaunchDisplayId, mTargetStack.mStackId);
return START_SUCCESS;
也没有什么好解释的,具体启动和显示的过程我们单独写一片文章分析