从源码角度看Activity launchMode与Stack/Task

简介

(本篇文章根据大家之前的留言反馈,代码分析这边只列出重要的代码,这样文章的篇幅不会太长太枯燥,如果想看全部代码的同学可以根据代码路径自己在本地看;同时有人反馈应该多加些和应用开发有关的知识,我这边做了些相关的调整,希望能对大家的胃口~
欢迎大家留言,有好的建议请告诉我,我会在以后的文章做出调整的;
写专栏写的很happy,还有很多话题想和大家分享,欢迎大家来订阅~)

以下是正文:

AMS中最复杂的组件当属Activity,它不仅仅涉及到使用WMS来显示窗口、使用PMS来解析应用资源,同时也管理着与用户密切相关的Stack, Task这些概念,其中,所有Android开发者耳熟能详的launchMode概念就与之紧密关联。

这篇文章里,我将以launchMode为主线,通过深入分析各个Activity的启动模式,使大家不仅仅对各个模式下启动Activity的行为规则达到认知的程度,同时从源码的层次带着大家一起亲眼目睹AMS是如何管理并使用Stack, Task来处理不同模式的行为。

分析完launchMode的各个模式之后,相信大家会对Stack, Task的概念有了初步印象。随后我将会列出一些Activity调试时常用的adb命令输出信息的结构图。希望大家看到这些命令后能够亲自找设备去尝试,对着结构图去看看这些信息能够帮助大家对Activity的理解上一个层次。这些图片大家也可以保存收藏,方便DEBUG时快速定位信息所在处。

文章的最开始,照惯例把相应的数据类列出,这张图贯穿着整篇文章:

查看大图

从launchMode开始谈

launchMode是初中级Android开发者面试时Activity中必谈的话题,被问起的频率和Activity的生命周期不相上下。虽然如此,这个知识点就像高级算法一样,问的多用的少,网上的技术博客与一些Android书籍虽然谈起过,但只是生涩的列举出规则,好一些的博客会画出一些图或者给个Demo作实例,大家的理解都是处于很浅的了解层面,一段时间不用就会忘记。

只是生硬的记忆规则很无趣,本章节我不仅会放出不同启动模式下,Activity的启动造成Stack与Task的变化图,同时也将从源码的层次中,跟踪这些模式的实现。这篇文章分析的源码基于Android 7.0,涉及Activity launchMode的源码主要在ActivityStarter类中;总体的流程各个Android版本上都是一致的。

以下我们假设Activity #ABCD都是在同一个应用中的Activity

standard

查看大图

standard模式下,无论Task中是否存在将要被启动的Activity,新的Activity被新建,然后插入到当前的Task中

frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java

  • private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
  • IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
  • int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
  • // 初始化Activity的状态,启动模式在这里被初始化
  • setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
  • voiceInteractor);
  • ...
  • // 这里会计算出mSourceStack,即源Activity的Stack
  • // [mSourceStack为当前启动这个Activity所在的ActivityStack]
  • computeSourceStack();
  • ...
  • // 此处会拿到前台Activity所在的ActivityStack
  • // [topStack为当前启动这个Activity所在的ActivityStack]
  • final ActivityStack topStack = mSupervisor.mFocusedStack;
  • ...
  • // 是否要创建一个新的Task
  • // [newTask = false]
  • boolean newTask = false;
  • final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
  • ? mSourceRecord.task : null;
  • ...
  • if (mSourceRecord != null && mSourceRecord.task.stack.mStackId != PINNED_STACK_ID) {
  • // 计算出mTargetStack; 设置Activity所在的Task
  • final int result = setTaskFromSourceRecord();
  • if (result != START_SUCCESS) {
  • return result;
  • }
  • }
  • ...
  • // 在此ActivityStack中将要启动的Activity所在Task置顶
  • mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);
  • ...
  • if (mDoResume) {
  • if (!mLaunchTaskBehind) {
  • mService.setFocusedActivityLocked(mStartActivity, "startedActivity");
  • }
  • ...
  • // 将Stack置顶;启动这个Activity
  • mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
  • mOptions);
  • ...
  • }
  • private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
  • boolean doResume, int startFlags, ActivityRecord sourceRecord,
  • IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
  • ...
  • mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP;
  • ...
  • }
  • private int setTaskFromSourceRecord() {
  • final TaskRecord sourceTask = mSourceRecord.task;
  • // 是否要替换要插入的ActivityStack
  • // [moveStackAllowed]
  • final boolean moveStackAllowed = sourceTask.stack.topTask() != sourceTask;
  • if (moveStackAllowed) {
  • mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.task,
  • mOptions);
  • }
  • // 获取源Activity所在的ActivityStack
  • if (mTargetStack == null) {
  • mTargetStack = sourceTask.stack;
  • }
  • // 将要插入的ActivityStack移动到顶部
  • if (mDoResume) {
  • mTargetStack.moveToFront("sourceStackToFront");
  • }
  • final TaskRecord topTask = mTargetStack.topTask();
  • ...
  • // 被启动的Activity设置与源Activity相同的Task
  • mStartActivity.setTask(sourceTask, null);
  • }
  • boolean setFocusedActivityLocked(ActivityRecord r, String reason) {
  • ...
  • final ActivityRecord last = mFocusedActivity;
  • mFocusedActivity = r;
  • ...
  • if (mStackSupervisor.moveActivityStackToFront(r, reason + " setFocusedActivity")) {
  • // WMS设置前台APP窗口
  • mWindowManager.setFocusedApp(r.appToken, true);
  • }
  • }

frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

  • final void startActivityLocked(ActivityRecord r, boolean newTask, boolean keepCurTransition,
  • ActivityOptions options) {
  • TaskRecord rTask = r.task;
  • final int taskId = rTask.taskId;
  • ...
  • task = r.task;
  • // 将当前Activity加入到顶端
  • task.addActivityToTop(r);
  • // 将Task置顶
  • task.setFrontOfTask();
  • ...
  • }

frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

  • boolean moveActivityStackToFront(ActivityRecord r, String reason) {
  • ...
  • final TaskRecord task = r.task;
  • ...
  • // 将要启动的Activity所在Stack置顶
  • task.stack.moveToFront(reason, task);
  • return true;
  • }

流程总结:

  1. 初始化Activity的状态,确定Activity的启动模式
  2. 计算出源Activity所在Stack
  3. 确认不需要创建新的Task, 找到将要插入Activity的mTargetStack
  4. 在mTargetStack中,将旧的Task置顶
  5. 将mTargetStack置顶并且启动这个Activity

后面的源码流程大致和这个相似,只是有两个特殊的地方:

  1. singleTop, singleTask存在不启动新的Activity的情况,只会复用Task中的Activity并调用其onNewIntent方法
  2. singleTask, singleInstance存在不使用当前Task的情况,会在启动Activity之前创建新的Task

singleTop

查看大图

要启动的Activity在当前Task顶端

frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java

  • private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
  • IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
  • int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
  • // 初始化Activity的状态,启动模式在这里被初始化
  • setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
  • voiceInteractor);
  • ...
  • // 这里会计算出mSourceStack,即源Activity的Stack
  • // [mSourceStack为当前启动这个Activity所在的ActivityStack]
  • computeSourceStack();
  • ...
  • // 此处会拿到前台Activity所在的ActivityStack
  • // [topStack为当前启动这个Activity所在的ActivityStack]
  • final ActivityStack topStack = mSupervisor.mFocusedStack;
  • // 判断是否要新建一个Activity
  • // [dontStart=true]
  • final boolean dontStart = top != null && mStartActivity.resultTo == null
  • // 此处会判断当前顶端的Activity是否和要启动的Activity是否是同一类的Activity
  • && top.realActivity.equals(mStartActivity.realActivity)
  • && top.userId == mStartActivity.userId
  • && top.app != null && top.app.thread != null
  • // 此处判断singleTop的FLAG
  • && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
  • || mLaunchSingleTop || mLaunchSingleTask);
  • if (dontStart) {
  • ...
  • topStack.mLastPausedActivity = null;
  • if (mDoResume) {
  • // 将要启动的Activity Stack置顶
  • mSupervisor.resumeFocusedStackTopActivityLocked();
  • }
  • ...
  • // 调用onNewIntent方法
  • top.deliverNewIntentLocked(
  • mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
  • ...
  • return START_DELIVERED_TO_TOP;
  • }
  • }
  • final void deliverNewIntentLocked(int callingUid, Intent intent, String referrer) {
  • ...
  • if ((state == ActivityState.RESUMED
  • || (service.isSleepingLocked() && task.stack != null
  • && task.stack.topRunningActivityLocked() == this))
  • && app != null && app.thread != null) {
  • try {
  • ArrayList<ReferrerIntent> ar = new ArrayList<>(1);
  • ar.add(rintent);
  • // binder call到应用调用onNewIntent方法即可
  • app.thread.scheduleNewIntent(ar, appToken);
  • unsent = false;
  • } catch (RemoteException e) {
  • Slog.w(TAG, "Exception thrown sending new intent to " + this, e);
  • } catch (NullPointerException e) {
  • Slog.w(TAG, "Exception thrown sending new intent to " + this, e);
  • }
  • }
  • ...
  • }

注:从该源码可以看到,平时里别人常说的singleTop的Activity如果已经在Task顶端,那么不会再被调用,只会调用下onNewIntent方法,这些说辞在源码里能很直观的被发现

Task中未存在的Activity && 要启动的Activity不在Task顶端

(同standard模式启动Activity的流程一致)

singleTask

查看大图

Task中已存在的Activity

frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java

  • private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
  • IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
  • int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
  • // 初始化Activity的状态,启动模式在这里被初始化
  • setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
  • voiceInteractor);
  • ...
  • // 这里会计算出mSourceStack,即源Activity的Stack
  • // [mSourceStack为当前启动这个Activity所在的ActivityStack]
  • computeSourceStack();
  • ...
  • // 这里会返回启动当前Activity的源Activity
  • mReusedActivity = getReusableIntentActivity();
  • ...
  • if (mReusedActivity != null) {
  • ...
  • if (mStartActivity.task == null) {
  • mStartActivity.task = mReusedActivity.task;
  • }
  • ...
  • if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
  • || mLaunchSingleInstance || mLaunchSingleTask) { // singleTask的情况
  • // 这步的操作将会移除此Task中,从顶部到已启动的Activity之间,所有的其它Activity
  • // 清除操作是在TaskRecord中去做的,这里代码就不跟了
  • final ActivityRecord top = mReusedActivity.task.performClearTaskForReuseLocked(
  • mStartActivity, mLaunchFlags);
  • if (top != null) {
  • // 只在客户端调用onNewIntent方法
  • top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
  • mStartActivity.launchedFromPackage);
  • }
  • }
  • if (!mAddingToTask && mReuseTask == null) {
  • ...
  • // 将要启动的Activity所在Stack置顶
  • resumeTargetStackIfNeeded();
  • return START_TASK_TO_FRONT;
  • }
  • }
  • }
  • private ActivityRecord getReusableIntentActivity() {
  • // 这里如果是singleTask, singleInstance则为true
  • boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
  • (mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
  • || mLaunchSingleInstance || mLaunchSingleTask;
  • // [putIntoExistingTask]
  • putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
  • ActivityRecord intentActivity = null;
  • if (putIntoExistingTask) {
  • if (mLaunchSingleInstance) {
  • ...
  • } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
  • ...
  • } else {
  • intentActivity = mSupervisor.findTaskLocked(mStartActivity);
  • }
  • }
  • return intentActivity;
  • }

frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

```java
ActivityRecord findTaskLocked(ActivityRecord r) {
mTmpFindTaskResult.r = null;
...
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
// 进入Stack去寻找
stack.findTaskLocked(r, mTmpFindTaskResult);
...
}

top Created with Sketch.