startActivity(MainActivity.this,LoginActivity.class); LoginActivity配置的launchMode是何时解析的?
这道题想考察什么?
考察同学是否Activity的launchMode熟悉。
考生应该如何回答
启动模式相关源码都在ActivityStarter#startActivityUnchecked(), 我们对这个函数来分析一下:
注意下两个主要的属性 mLaunchFlags,mLaunchMode,后面的源码也是主要对着两个属性分析。
mLaunchFlags 包括启动的flag,比如FLAG_ACTIVITY_NEW_TASK,FLAG_ACTIVITY_CLEAR_TASK等,作用是表明如何去启动一个Activity,对栈的选择和处理。
mLaunchMode 表示启动模式,比如LAUNCH_SINGLE_INSTANCE,LAUNCH_SINGLE_TASK等。
初始化工作
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
ActivityRecord[] outActivity) {
// 初始化 mLaunchFlags mLaunchMode
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor);
// 一.计算 mLaunchFlags
computeLaunchingTaskFlags();
//赋值 mSourceTask
computeSourceStack();
mIntent.setFlags(mLaunchFlags);
... ... ...
我们看下 computeLaunchingTaskFlags 方法
private void computeLaunchingTaskFlags() {
... ... ...
// mSourceRecod 指的是 启动者,(注意区别于 被启动者 mStartActivity) mSourceRecord为null,表示我们不是从一个Activity来启动的
// 可能是从 一个Service 或者 ApplicationContext 来的
if (mSourceRecord == null) {
if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {
//mInTask mSourceRecord 都为null, 表示 不是从一个Activity 去启动另外一个Activity,所以不管什么
//都加上 FLAG_ACTIVITY_NEW_TASK
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
} else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
// 如果 启动者 自己是 SINGLE_INSTANCE , 那么不管被启动的Activity是什么模式,mLaunchFlags 都加上 FLAG_ACTIVITY_NEW_TASK,
// 这个新 Activity 需要运行在 自己的 栈内
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
} else if (isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
//如果launchMode是 SINGLE_INSTANCE 或者 SINGLE_TASK; mLaunchFlags 添加 FLAG_ACTIVITY_NEW_TASK
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
}
如果启动者mSourceRecord=null,例如在一个Service启动了一个Activity,那么mLaunchFlags增加FLAG_ACTIVITY_NEW_TASK
如果启动者mSourceRecord是一个 SingleInstance 类型的Activity,那么被启动者的mLaunchFlags就会加上 FLAG_ACTIVITY_NEW_TASK
如果被启动者mStartActivity是SINGLE_INSTANCE或者SINGLE_TASK 类型的Activity,被启动者的mLaunchFlags都会加上FLAG_ACTIVITY_NEW_TASK
getResuableIntentActivity
这一部分还是startActivityUnchecked方法的一个片段,紧接着第一部分的源码
//还是在 startActivityUnchecked 里面
... ... ...
// 1. 为SINGLE_INSTANCE查找可以复用的Activity
// 2. 为 只有FLAG_ACTIVITY_NEW_TASK并且没有MULTI_TASK的
// 3. SINGLE_TASK 查找可以添加的栈
ActivityRecord reusedActivity = getReusableIntentActivity();
... ... ...
我们先跳出startActivityUnchecked方法,去查看下getResuableIntentActivity方法。
private ActivityRecord getReusableIntentActivity() {
//putIntoExistingTask为true的条件
//1.当启动模式为SingleInstance;
//2.当启动模式为SingleTask;
//3.使用了Intent.FLAG_ACTIVITY_NEW_TASK标签,并且没有使用FLAG_ACTIVITY_MULTIPLE_TASK标签
boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
(mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK);
putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
ActivityRecord intentActivity = null;
if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
... ... ...
} else if (putIntoExistingTask) { //putIntoExistingTask为true时的策略
if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
// SINGLE_INSTANCE 模式下去寻找,这里目的是findActivityRecord
intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
mStartActivity.isActivityTypeHome());
} else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
... ...
} else {
//这里要区别于singleInstance调用的方法!!!这里目的是findTaskRecord
// Otherwise find the best task to put the activity in.
intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId);
}
}
return intentActivity;
}
mLaunchMode是SingleInstance时,走mSupervisor.findActivityLocked;剩余情况下,比如我们的mStartActivity是一个standard模式的Activity,且只加上了FLAG_ACTIVITY_NEW_TASK的flag,会执行mSupervisor.findTaskLocked。
最合适的可重用栈
void findTaskLocked(ActivityRecord target, FindTaskResult result) {
... ... ...
// 注意这是倒序遍历 mTaskHistory
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
... ... ...
} else if (!isDocument && !taskIsDocument
&& result.r == null && task.rootAffinity != null) {
//检查 是不是 相同的 taskAffinity
if (task.rootAffinity.equals(target.taskAffinity)) {
//当我们找到taskAffinity符合的栈之后,并没有立马break,而是继续去寻找,说明task的index越小,表示更适合
result.r = r;
result.matchedByRootAffinity = true;
}
} else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
}
}
最合适的栈 需要满足两个条件
1.Activity的taskAffinity和我们的task的rootAffinity相等
2.不同的task的rootAffinity可能是相等的,倒序遍历找到index最小的,也是最合适的
reusedActivity的处理
我们接着分析startActivityUnchecked代码
// 三. 利用可以 复用的Activity 或者 复用栈
if (reusedActivity != null) {
... ... ...
// 如果是 SingleInstance 或者 SingleTask 或者 含有 FLAG_ACTIVITY_CLEAR_TOP 标识
//我们可以判断出来 SingleInstance 或者 SingleTask 含有 FLAG_ACTIVITY_CLEAR_TOP 的效果
if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isDocumentLaunchesIntoExisting(mLaunchFlags)
|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
//拿 reuseActivity 的栈
final TaskRecord task = reusedActivity.getTask();
// 比如 SingleTask 移除要启动的Activity之前的所有Activity
final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
mLaunchFlags);
if (reusedActivity.getTask() == null) {
reusedActivity.setTask(task);
}
if (top != null) {
if (top.frontOfTask) {
// Activity aliases may mean we use different intents for the top activity,
// so make sure the task now has the identity of the new intent.
top.getTask().setIntent(mStartActivity);
}
//这里是 SingleInstance 或者 SingleTask ,会执行onNewIntent
deliverNewIntent(top);
}
}
... ... ...
//启动者和被启动者是同一个
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;
}
if (reusedActivity != null) {
//这里会去判断几种情况 singleTask singleInstance 和 singleTop
setTaskFromIntentActivity(reusedActivity);
if (!mAddingToTask && mReuseTask == null) {
//singleInstance singleTask 都会走这里
//1.比如要启动的Activity是singleTask,且刚好在reusedActivity的栈内
//2.或者一个singleInstance模式的Activity再次被启动
resumeTargetStackIfNeeded();
if (outActivity != null && outActivity.length > 0) {
outActivity[0] = reusedActivity;
}
return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;
}
}
}
继续来查看下setTaskFromIntentActivity这个方法
private void setTaskFromIntentActivity(ActivityRecord intentActivity) {
if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
//如果是 FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TASK
final TaskRecord task = intentActivity.getTask();
//清空task
task.performClearTaskLocked();
mReuseTask = task;
mReuseTask.setIntent(mStartActivity);
} else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
//如果是 SingleInstance 或者 SingleTask 走这里,清空栈内要启动的Activity之前的所有Activity们。
ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity,
mLaunchFlags);
//如果top == null 继续走,不为null,就结束了这个方法
if (top == null) {
... ... ...
}
} else if (mStartActivity.realActivity.equals(intentActivity.getTask().realActivity)) {
// 判断是否是 SingleTop 模式
// 这种情况如何复现? SingleTop + FLAG_ACTIVITY_NEW_TASK + taskAffinity。
// FLAG_ACTIVITY_NEW_TASK + taskAffinity去指定一个特定存在的栈,且栈顶是我们要启动的singleTop模式的activity
if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
|| LAUNCH_SINGLE_TOP == mLaunchMode)
&& intentActivity.realActivity.equals(mStartActivity.realActivity)) {
if (intentActivity.frontOfTask) {
intentActivity.getTask().setIntent(mStartActivity);
}
deliverNewIntent(intentActivity);
} else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) {
... ... ...
}
} else if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
... ... ...
} else if (!intentActivity.getTask().rootWasReset) {
... ... ...
}
}
前提是reusedActivity不为null,看两种情况:
1.如果是SingleTask、SingleInstance模式的Activity,则调用performClearTaskLocked方法,把要启动的Activity之前的所有Activity都清除掉。
2.reusedActivity的启动模式恰好是SingleTop,且也是我们需要启动的Activity,执行 deliverNewIntent。
上述的两种情况能够满足下面的if判断 !mAddingToTask && mReuseTask == null ,然后return结束。
判断SingleTop模式
继续看 startActivityUnchecked 后面的代码,这一部分是针对SingleTop模式的处理。
... ... ...
//下面这段英文解释的很好(SingleTop模式),当我们要启动的Actiivty恰好是当前栈顶的Activity,检查是否只需要被启动一次
// If the activity being launched is the same as the one currently at the top, then
// we need to check if it should only be launched once.
final ActivityStack topStack = mSupervisor.mFocusedStack;
final ActivityRecord topFocused = topStack.getTopActivity();
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
|| isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK));
// SINGLE_TOP SINGLE_TASK,要启动的Activiy恰好在栈顶
// dontStart为true,表示不会去启动新的Activity,复用栈顶的Activity
if (dontStart) {
// For paranoia, make sure we have correctly resumed the top activity.
topStack.mLastPausedActivity = null;
if (mDoResume) {
// resume Activity
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;
}
deliverNewIntent(top);
// 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(), preferredWindowingMode,
preferredLaunchDisplayId, topStack);
return START_DELIVERED_TO_TOP;
}
栈的复用和新建
继续分析startActivityUnChecked方法的最后一部分,这部分主要是有关于栈是否需要新建。
... ... ...
//必要条件是有FLAG_ACTIVITY_NEW_TASK
if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
//要建立新栈或者使用已经存在的栈,FLAG_ACTIVITY_NEW_TASK是必要条件
newTask = true;
result = setTaskFromReuseOrCreateNewTask(taskToAffiliate, topStack);
} else if (mSourceRecord != null) {
//把被启动的mStartActivity放在启动者mSourceRecord所在的栈上
result = setTaskFromSourceRecord();
}
... ... ...
setTaskFromReuseOrCreateNewTask 方法
private int setTaskFromReuseOrCreateNewTask(
TaskRecord taskToAffiliate, ActivityStack topStack) {
mTargetStack = computeStackFocus(mStartActivity, true, mLaunchFlags, mOptions);
if (mReuseTask == null) {
//新建一个栈
final TaskRecord task = mTargetStack.createTaskRecord(
mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
mVoiceInteractor, !mLaunchTaskBehind /* toTop */, mStartActivity, mSourceRecord,
mOptions);
addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds);
} else {
//用旧栈
addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask");
}
... ... ...
总结
- 新建栈或者复用栈的必要条件是FLAG_ACTIVITY_NEW_TASK。SingleTask,SingleInstance会为mLaunchFlags自动添加FLAG_ACTIVITY_NEW_TASK。也就是说他们都有存在不使用当前栈的可能。
- 新建栈或者复用已经存在栈的充分必要条件是什么?
- FLAG_ACTIVITY_NEW_TASK + taskAffinity(taskAffinity必须与当前显示的栈的rootAffinity不相同,taskAffinity默认是包名)。
- FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_MULTIPLE_TASK 这是必定会新建一个栈的。
- SingleTask可以理解成FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TOP + (taskAffinity == 该应用包名)。补充一下,FLAG_ACTIVITY_CLEAR_TOP的作用是啥?比如我们要启动的SingleTask模式的Activity已经在栈内,且不在栈的头部,会把之前的Activity全部出栈。
- SingleInstance比较特殊,首先SingleInstance模式的Activity会被加上FLAG_ACTIVITY_NEW_TASK,这一点和SingleTask一样。特殊的地方其一在于如果启动过了,会去遍历找相等的Activity,查找过程不一样。而不像SingleTask是去找"合适的"栈,根据taskAffinity来查找。其二在于SingleInstance一个栈只能存放一个Activity,能做到这个的原因是我们在根据taskAffinity找到合适的栈的时候,如果发现是SingleInstance模式Activity的栈,直接忽略。
最后
我整理了一套Android面试题合集,除了以上面试题,还包含【Java 基础、集合、多线程、虚拟机、反射、泛型、并发编程、Android四大组件、异步任务和消息机制、UI绘制、性能调优、SDN、第三方框架、设计模式、Kotlin、计算机网络、系统启动流程、Dart、Flutter、算法和数据结构、NDK、H.264、H.265.音频编解码、FFmpeg、OpenMax、OpenCV、OpenGL ES】
有需要的朋友可以扫描下方二维码,免费领取全部面试题+答案解析!!!
![](https://i-blog.csdnimg.cn/blog_migrate/a71ab54c5dd2b7040389367ce214f4c8.png)