本文为原创文章,转载请注明出处,原创不易,且转且珍惜
1.前言
Activity栈管理是AMS的另一个重要功能,栈管理又和Activity的启动模式和startActivity时所设置的Flag息息相关,Activity栈管理的主要处理逻辑是在ActivityStarter#startActivityUnchecked
方法中,本文也会围绕着这个方法进进出出,反复摩擦,直到脑海中都是它的形状。goolge的工程师起名还是很讲究的,为什么要带Unchecked呢? Unchecked-不确定,是因为在执行这个方法时,我要启动哪个Activity还没决定呢,具体为什么,我想看过这篇文章你就明白了。
2.Activity栈管理相关类
2.1ActivityStackSupervisor
顾名思义,Activity栈的功能提供者和管理者
2.2.ActivityDisplay
表示一个屏幕,Android支持三种屏幕:主屏幕,外接屏幕(HDMI等),虚拟屏幕(投屏)。一般情况下,即只有主屏幕时,ActivityStackSupervisor与ActivityDisplay都是系统唯一
2.3.TaskRecord
ActivityTask记录, Task是我们管理Activity栈的重要单元,它的表现形式与逻辑和Activity启动模式息息相关,也是本文重点要分析的
2.4.ActivityStack
针对ActivityRecord 和 TaskRecord进行管理,记录ActivityRecord的状态和TaskRecord的状态。在Android N之前只有两种ActivityStack:homeStack(launcher和recents Activity)和其他。Android N开始有5种,增加了DockedStack(分屏Activity)、PinnedStack(画中画Activity)、freeformstack(自由模式Activity),虽然它名字叫ActivityStack,但是跟我们熟知的数据结构中的栈基本上没啥关系,这也是有可能会增加一点理解难度的地方
2.5.关系图:
先说一下关系:
- 一个ActivityDisplay包含多个ActivityStack
- 一个ActivityStack包含多个TaskRecord
- 一个TaskRecord包含多个ActivityRecord
3.启动模式
3.1.standard
标准启动模式,启动Activity时依次向栈顶添加ActivityRecord,返回时依次推出,示例:AActivity打开BActivity打开CActivity
这是基本的Activity启动模式,需要注意的点不太多
3.1.1.Intent.FLAG_ACTIVITY_CLEAR_TOP
还是刚才的案例,如果依次打开AActivity->BActivity->CActivity->DActivity,此时在DActivity打开AActivity时Intent添加Flag Intent.FLAG_ACTIVITY_CLEAR_TOP
,系统会从BActivity开始依次将Task中的Activity依次销毁,直到DActivity,因为DActivity处于活跃状态,因此会先执行onPause,在onPause后,会销毁原来的AActivity,然后打开新的AActivity,最后执行DActivity的onStop和onDestory
流程图如下:
log:
3.1.2.源码分析
3.1.2.1.加入TaskRecord
摘抄ActivityStarter#startActivityUnchecked
部分代码如下:
if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
newTask = true;
// zhangyulong 使用一个旧的Task 或者新建一个
result = setTaskFromReuseOrCreateNewTask(
taskToAffiliate, preferredLaunchStackId, topStack);
} else if (mSourceRecord != null) {
// zhangyulong 使用源Activity的task
result = setTaskFromSourceRecord();
} else if (mInTask != null) {
// zhangyulong 使用启动时传递的task
result = setTaskFromInTask();
} else {
// zhangyulong 理论上的可能,不可能走到这里
setTaskToCurrentTopOrCreateNewTask();
}
if (result != START_SUCCESS) {
return result;
}
因为我们使用标准模式启动,因此,resultTo
和mSourceRecord
均不为空,这段逻辑会执行setTaskFromSourceRecord
:
private int setTaskFromSourceRecord() {
...
addOrReparentStartingActivity(sourceTask, "setTaskFromSourceRecord");
return START_SUCCESS;
}
addOrReparentStartingActivity
最终会执行TaskRecord#addActivityAtIndex
:
void addActivityAtIndex(int index, ActivityRecord r) {
...
mActivities.add(index, r);
...
}
向对应的ActivityRecord中的mActivities添加本条记录,完成加入TaskRecord的操作
3.1.2.2.standard + Intent.FLAG_ACTIVITY_CLEAR_TOP
回到ActivityStarter#startActivityUnchecked
,摘抄部分逻辑如下:
if (!mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0) {
// 将ActivityTask目标Actiivty之上的Activity全部清空,返回值top为可以复用的Activity
ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags);
mKeepCurTransition = true;
// 如果可复用的Activity不为空,直接调用它的onNewIntent方法并将其resume
if (top != null) {
ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask());
deliverNewIntent(top);
mTargetStack.mLastPausedActivity = null;
if (mDoResume) {
mSupervisor.resumeFocusedStackTopActivityLocked();
}
ActivityOptions.abort(mOptions);
return START_DELIVERED_TO_TOP;
}
}
重点看performClearTaskLocked
,这里是将目标Activity顶部元素清空的逻辑
/***
* newR: 需要启动的新Activity
* launchFlags: 新Activity的启动模式
*/
final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
int numActivities = mActivities.size();
for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = mActivities.get(activityNdx);
if (r.finishing) {
continue;
}
// 在目标Task中找到了和新Activity相同的记录
if (r.realActivity.equals(newR.realActivity)) {
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);
}
// 执行finishActivityLocked,如果Activity已经stop,会直接执行onDestroy,
// 如果Activity还在活跃,则会先执行onPause
if (mStack != null && mStack.finishActivityLocked(
r, Activity.RESULT_CANCELED, null, "clear-task-stack", false)) {
--activityNdx;
--numActivities;
}
}
// ActivityInfo.LAUNCH_MULTIPLE == standrad
// 如果新Activity的launchMode是standard,且launchFlag没有FLAG_ACTIVITY_SINGLE_TOP,则将之前task
// 内的activity也结束,以便建立新的
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);
}
// 返回空,说明不执行onNewIntent
return null;
}
}
return ret;
}
}
return null;
}
看完这部分代码,我想FLAG_ACTIVITY_CLEAR_TOP是怎么工作的大家也就明白了。
3.2.singleTop
singleTop:栈顶唯一,它和standrad的区别在于,如果是standrad模式,在栈顶启动一个相同的Activity,会创建一个新的Activity实例,如果是singleTop模式,在栈顶启动相同的Activity则只会调用原有Activity的onNewIntent,如果原Activity不在栈顶,那么表现形式就和standrad相同
3.2.1.流程图:
3.2.1.1.原Activity不在栈顶
3.2.1.2.原Activity在栈顶
3.2.2.singleTop + Intent.FLAG_ACTIVITY_CLEAR_TOP
现在设想一种场景,AActivitylanchMode为singleTop,在AActivity的基础上依次打开了BActivity、CActivity, 在CActivity再次打开AActivity,但打开时设置Intent属性Intent.FLAG_ACTIVITY_CLEAR_TOP
,此时的启动流程:
3.2.3.log
3.2.3.1.原Activity不在栈顶
3.2.3.2.原Activity在栈顶
3.2.3.3.原Actiivty在栈顶且设置Intent.FLAG_ACTIVITY_CLEAR_TOP
3.2.3.源码分析
我们又要进入ActivityStarter#startActivityUnchecked
方法了, startActivityUnchecked:你要对我负责555...
摘抄startActivityUnchecked方法中关于singleTop模式的处理如下:
// 要启动的Activity正好是当前在栈顶的Activity
// 当前聚焦的ActivityStack
final ActivityStack topStack = mSupervisor.mFocusedStack;
// 当前聚焦的ActivityStack中的栈顶Actiivty
final ActivityRecord topFocused = topStack.topActivity();
// 当前栈顶Activity
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);
// dontStart为true说明可以直接复用栈顶Activity
if (dontStart) {
// 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;
}
// zhangyulong singleTop sigleTask onNewIntent 执行
deliverNewIntent(top);
return START_DELIVERED_TO_TOP;
}
需要注意的是,当我们设置目标Activity launchMode是singleTop时,判断条件用的是mLaunchSingleTop,而(mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP是指启动时设置的Intent的Flag属性。这两种方式都能达到singleTop的效果。
3.2.3.1.singleTop + Intent.FLAG_ACTIVITY_CLEAR_TOP
这部分的逻辑处理和standard时差不多,还是performClearTaskLocked
方法,这个方法在3.1.2.2章节已经分析过了,这里摘抄部分逻辑如下:
// ActivityInfo.LAUNCH_MULTIPLE == standrad
// 如果新Activity的launchMode是standard,且launchFlag没有FLAG_ACTIVITY_SINGLE_TOP,则将之前task
// 内的activity也结束,以便建立新的
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);
}
// 返回空,说明不执行onNewIntent
return null;
}
}
因为我们的launchMode是FLAG_ACTIVITY_SINGLE_TOP,条件不成立,不会销毁命中的原Activity,但原Actiivty上面的记录均已销户,此时它已经是栈顶Activity了,继续执行会执行到startActivityUnchecked
方法中3.2.3章节部分,和上面的逻辑就一致了。
3.3 singleTask
singleTask: 栈内唯一,如果Activity设置了launchMode为singleTask,那么在整个ActivityStack中有且仅有一个实例存在。有些朋友看到它名字叫singleTask,就想当然的认为它是Task内唯一的,我们不要被它的名字骗了。需要注意的一点是,singleTask模式启动是默认clearTop的。
3.3.1 启动流程
场景一
假设AActivity的launchMode为singleTask,AActivity后依次启动BActivity和CActivity, CActivity又启动了AActivity,
那么这个过程经历的流程如下:
log:
场景二:
假设AActivity和BActivity都是Standard,CActivity为singleTask,CActiivty再次启动CActivity,这个过程的启动流程:
log:
场景三:
看完前面两种场景,肯定会有同学不服气,你不是说singleTask是栈内唯一么,这两种场景都是task内的处理啊,那不就应该是task内唯一么,你说栈内唯一拿出证据来啊!别着急,证据马上来。
现在假设AActivity是singleTask, BActivity是standard, BActivity打开CActivity时创建新的Task, 然后CActivity再次打开AActivity
流程如下:
1.AActivity在其自身所在task启动BActivity
2.BActivity在启动CActivity时创建新的Task
3.CActivity启动AActivity时将AActivity所在的Task移到顶部
4.AActivity将BActivity清除并重新启动
流程图:
看下log是不是这样:
log也验证了这个说法的正确性,从这个案例中我们看出,虽然AB在一个task, C在另一个task,但C启动A的时候,并没有在其自身的task启动,而是操作AB所在的task。因此,singeTask是栈内唯一的。
3.3.2 singeTask源码分析
再进入一次ActivityStarter#startActivityUnchecked
一次,此时startActivityUnchecked
内心活动:别进了,再进就怀孕了!!
为什么说singleTask自带clearTop属性呢? 看下startActivityUnchecked
的这段逻辑:
// 如果启动Intent设置了FLAG_ACTIVITY_CLEAR_TOP或者目标Activity启动模式为singleInstance或者singleTask,执行以下逻辑
if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isDocumentLaunchesIntoExisting(mLaunchFlags)
|| mLaunchSingleInstance || mLaunchSingleTask) {
final TaskRecord task = reusedActivity.getTask();
// 将与新Actiivty在Task内相同的Activity的顶部元素清空
final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
mLaunchFlags);
...
}
那么task切换到栈顶是在哪里呢,这段逻辑执行完成后,就会执行到Task切换的逻辑了,代码:
// 将目标Activity所在Task移动到栈顶
reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);
当前面的逻辑完成后,可复用用的Activity就已经在Task顶部,而Task也已经在Stack顶部,完事俱备,只欠东风,继续执行startActivityUnchecked
会执行到3.2.3的内容,和singleTop的处理是一致的,最终回调了目标Activity的onNewIntent。
3.4 singleInstance
singleInstance这个启动模式比sigleTask更NB一些,它不光在栈内唯一,而且还独占一个Task,一看就是那种有独立办公室的老板,跟其他打工人完全不是一个等级的。关于singleInstance的栈管理和切换,你可以把它理解成只有一个singleTask的Activity存在的Task就比较好理解了,上面我们也已经分析过了。
4. Intent.FLAG_ACTIVITY_NEW_TASK、taskAffinity、新Task的创建
先看几个有意思的案例:
现在有两个Activity,分别是AActivity和BActivity,我们先设置A和B均为standard,在A启动B时设置Intent.FLAG_ACTIVITY_NEW_TASK
,跳转时Task会新建么?
看一下log:
从图上我们看到,A和B的TaskId都是305,也就是没有创建新的Task,怎么回事,Intent.FLAG_ACTIVITY_NEW_TASK怎么失效了?
这个时候把B的launchMode设置为singleTask呢?
看一下log:
依然没有生效!!
这个时候保持B的launchMode为singleTask, 设置B的taskAffinity为".b"试一下:
生效了!!
这个时候把Intent.FLAG_ACTIVITY_NEW_TASK取消,保持B的launchMode为singleTask, 设置B的taskAffinity为".b"试一下:
生效了!
这个时候把Intent.FLAG_ACTIVITY_NEW_TASK取消,修改B的launchMode为standard, 设置B的taskAffinity为".b"试一下:
没生效!
这个时候把B的taskAffinity删掉,设置B为singleInstace试一下:
又生效了!!
看到这里是不是感觉已经晕了,那到底啥时候生效晒时候失效啊!别着急,看完源码我们再做总结
我们又要进入ActivityStarter#startActivityUnchecked
方法了, startActivityUnchecked:不挣扎了,已经有你的形状了...
4.1 Intent.FLAG_ACTIVITY_NEW_TASK的自动设置
startActivityUnchecked前面几行代码执行了一个叫做computeLaunchingTaskFlags
的方法,这个方法的作用是根据新Activity的launchMode对launchFlag做处理:
private void computeLaunchingTaskFlags() {
...
if (mInTask == null) {
if (mSourceRecord == null) {
...
} else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
// 如果源Activity是singleInstance,则新启动Activity时自动添加launchFlag FLAG_ACTIVITY_NEW_TASK
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
} else if (mLaunchSingleInstance || mLaunchSingleTask) {
// 如果新Activity的launchMode是singleInstace或者singleTask,则新启动Activity时自动添加launchFlag FLAG_ACTIVITY_NEW_TASK
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
}
}
也就是说,如果一个Actiivty是singleInstacne的,那么不管是别人启动它还是它启动别人,都会自动添加启动flag FLAG_ACTIVITY_NEW_TASK, 如果是singeTask,则只有别人启动它时才会这样设置
4.2 taskAffinity的识别
4.1部分的逻辑执行后,startActivityUnchecked
会执行getReusableIntentActivity
方法,这个方法主要是寻找ActivityStack中是否有可复用的Task, 返回值会可复用Task的顶部元素:
private ActivityRecord getReusableIntentActivity() {
// 设置了launchFlag为FLAG_ACTIVITY_NEW_TASK或者 launchMode为singleInstance或singleTask
boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
(mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| mLaunchSingleInstance || mLaunchSingleTask;
// inTask为null 且requestCode小于0(即resultTo == null)
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) {
// 如果launchMode为singleInstance,只要当前状态下Stack中有和要启动的Activity相同的记录,就说明可以复用
intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
mStartActivity.isHomeActivity());
} else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
// 没研究
intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
!mLaunchSingleTask);
} else {
// 其他情况查找Stack中是否有适用的Task和可复用的Actiivty
intentActivity = mSupervisor.findTaskLocked(mStartActivity, mSourceDisplayId);
}
}
return intentActivity;
}
findActivityLocked
逻辑比较简单,就是在整个Stack中遍历Activity作对比,重点看ActivitySuperVisor#findTaskLocked
,ActivitySuperVisor#findTaskLocked
中调用了ActivityStack#findTaskLocked
,看一下重要逻辑:
void findTaskLocked(ActivityRecord target, FindTaskResult result) {
...
} else if (!isDocument && !taskIsDocument
&& result.r == null && task.rootAffinity != null) {
// 如果Task的rootAffinity和新Activity的taskAffinity匹配,则说明有可复用的栈
// ,task的rootAffinity一般由底部Actiivty决定,不特意设置的话,一般使用包名
if (task.rootAffinity.equals(target.taskAffinity)) {
result.r = r;
result.matchedByRootAffinity = true;
}
} else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
}
}
这里就是匹配taskAffinity的地方。回到getReusableIntentActivity
方法,说一下它的返回值逻辑:
- 如果launchMode是singleInstance,则判断当前stack中是否有相同Actiivty,如果有则返回对应Actiivty,否则是null
- 如果launchMode是其他,则判断当前stack中是否有可以匹配其affinity的Task,如果有则返回对应Task顶部Activity,否则是null
4.3 是否创建新task的识别
如果getReusableIntentActivity
方法返回值不为null,startActivityUncheck
后面的逻辑会执行setTaskFromReuseOrCreateNewTask
方法:
private void setTaskFromIntentActivity(ActivityRecord intentActivity) {
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();
// 设置启动新Actiivty时所使用的task
mReuseTask = task;
mReuseTask.setIntent(mStartActivity);
}
...
}
注意,这个方法执行的必要条件是getReusableIntentActivity
方法返回值不为null,入参intentActivity即是getReusableIntentActivity
方法的返回值。
因此,假如mReuseTask 为null,则启动Actiivty时会创建新的task,否则向mReuseTask 中添加,逻辑如下:
private int setTaskFromReuseOrCreateNewTask(
TaskRecord taskToAffiliate, int preferredLaunchStackId, ActivityStack topStack) {
mTargetStack = computeStackFocus(
mStartActivity, true, mLaunchBounds, mLaunchFlags, mOptions);
if (mReuseTask == null) {
// 创建新的Task
final TaskRecord task = mTargetStack.createTaskRecord(
mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
mVoiceInteractor, !mLaunchTaskBehind /* toTop */, mStartActivity.mActivityType);
// 向新Task中添加
addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
...
} else {
// 向旧task中添加
addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask");
}
...
return START_SUCCESS;
}
4.4 总结
通过上面的代码分析,我们可以总结出在Activity启动过程中创建新Task的条件:
- standard、singleTop模式 Intent.FLAG_ACTIVITY_NEW_TASK 和taskAffinity必须同时设置
- sinlgeTask模式 只需设置taskAffinity,Intent.FLAG_ACTIVITY_NEW_TASK 可有可无
- singeInstance Intent.FLAG_ACTIVITY_NEW_TASK 和taskAffinity均可有可无