从源码角度看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.