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

简介

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

这篇文章里,我将以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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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

1
2
3
4
5
6
7
8
9
10
11
12
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

1
2
3
4
5
6
7
8
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ActivityRecord findTaskLocked(ActivityRecord r) {
mTmpFindTaskResult.r = null;
...
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> 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);
...
}
}
...
return mTmpFindTaskResult.r;
}

注:这里可以看到,singleTask的模式下,如果该task中已经了被启动了的Activity,那么AMS将会首先清除掉中间的其他Activity,最后调用该Activity的onNewIntent方法。

Task中未存在的Activity

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

singleInstance

singleInstance模式中,单个Task中只存在一个Activity;如果Task中已存在该Activity,那么再次启动不会有反应;如果Task中未存在该Activity,则会新建一个Task,并且将Activity插入到新建的Task中

查看大图

Task中已存在的Activity

同singleTop要启动的Activity已在Task顶端的流程一致,直接调用onNewIntent方法

Task中未存在的Activity

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
boolean newTask = false;
if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
newTask = true;
setTaskFromReuseOrCreateNewTask(taskToAffiliate);
...
}
...
// 在此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 setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate) {
// 获取目标Stack
mTargetStack = computeStackFocus(mStartActivity, true, mLaunchBounds, mLaunchFlags,
mOptions);
if (mReuseTask == null) {
// 新建一个TaskRecord
final TaskRecord task = mTargetStack.createTaskRecord(
mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
mNewTaskIntent != null ? mNewTaskIntent : mIntent,
mVoiceSession, mVoiceInteractor, !mLaunchTaskBehind /* toTop */);
// 设置Activity的Task
mStartActivity.setTask(task, taskToAffiliate);
...
}
}

注:可以看到,singleInstance模式和standard的模式区别在于,在正常的启动一个Activity前,会先创建一个task,然后再在新的task上启动这个Activity

谈谈”dumpsys activity a”

一个合格的Android开发者,除了需要了解最最基本的四大组件生命周期之外,还需要能够熟练的使用adb进行调试。Android系统的adb调试桥配备了强大的dumpsys命令,系统中的运行状况可以覆盖到各个模块,所以平日里的开发中,除了logcat输出的日志信息以外,dumpsys的输出也能够助力我们解决问题。

dumpsys命令中,最常用的模块当属activity,这个activity指的是AMS模块,其中包含了四大组件以及进程相关的信息,它的输出太多了。这里,我们只想看看Activity的状态只需输入adb shell dumpsys activity a,AMS的细节很多,好奇的话大家可以输入adb shell dumpsys activity -h看看都有什么细分的模块。

以下,是我在手机桌面并且未开启其它APP打印出的信息,具体解释请看下面的表格和结构图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
Display #0 (activities from top to bottom):
Stack #0:
Task id #1
* TaskRecord{58b88f5 #1 A=com.miui.home U=0 sz=1}
userId=0 effectiveUid=u0a17 mCallingUid=1000 mCallingPackage=android
affinity=com.miui.home
intent={act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10800000 cmp=com.miui.home/.launcher.Launcher}
realActivity=com.miui.home/.launcher.Launcher
autoRemoveRecents=false isPersistable=false numFullscreen=1 taskType=1 mTaskToReturnTo=0
rootWasReset=false mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
Activities=[ActivityRecord{3504f99 u0 com.miui.home/.launcher.Launcher t1}]
askedCompatMode=false inRecents=true isAvailable=true
lastThumbnail=null lastThumbnailFile=/data/system/recent_images/1_task_thumbnail.png
stackId=0
hasBeenVisible=true mResizeable=false firstActiveTime=1511598102183 lastActiveTime=1511598102183 (inactive for 19s)
* Hist #0: ActivityRecord{3504f99 u0 com.miui.home/.launcher.Launcher t1}
packageName=com.miui.home processName=com.miui.home
launchedFromUid=0 launchedFromPackage=null userId=0
app=ProcessRecord{c6486e5 4281:com.miui.home/u0a17}
Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10800000 cmp=com.miui.home/.launcher.Launcher }
frontOfTask=true task=TaskRecord{58b88f5 #1 A=com.miui.home U=0 sz=1}
taskAffinity=com.miui.home
realActivity=com.miui.home/.launcher.Launcher
baseDir=/system/priv-app/MiuiHome/MiuiHome.apk
dataDir=/data/user/0/com.miui.home
stateNotNeeded=true componentSpecified=false mActivityType=1
compat={440dpi} labelRes=0x7f0d0004 icon=0x7f020098 theme=0x7f070000
config={1.0 ?mcc?mnc en_US ldltr sw392dp w392dp h678dp 440dpi nrml long port finger -keyb/v/h -nav/h s.4 themeChanged=0 themeChangedFlags=0}
stackConfigOverride={1.0 ?mcc?mnc ?locale ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/? themeChanged=0 themeChangedFlags=0}
taskDescription: iconFilename=null label="null" color=ffe6e6e6
launchFailed=false launchCount=0 lastLaunchTime=-4h48m42s487ms
haveState=false icicle=null
state=RESUMED stopped=false delayedResume=false finishing=false
keysPaused=false inHistory=true visible=true sleeping=false idle=true
fullscreen=true noDisplay=false immersive=false launchMode=2
frozenBeforeDestroy=false forceNewConfig=false
mActivityType=HOME_ACTIVITY_TYPE
waitingVisible=false nowVisible=true lastVisibleTime=-18s877ms
Running activities (most recent first):
TaskRecord{58b88f5 #1 A=com.miui.home U=0 sz=1}
Run #0: ActivityRecord{3504f99 u0 com.miui.home/.launcher.Launcher t1}
mResumedActivity: ActivityRecord{3504f99 u0 com.miui.home/.launcher.Launcher t1}
mLastPausedActivity: ActivityRecord{3504f99 u0 com.miui.home/.launcher.Launcher t1}
mFocusedActivity: ActivityRecord{3504f99 u0 com.miui.home/.launcher.Launcher t1}
mFocusedStack=ActivityStack{976a693 stackId=0, 1 tasks} mLastFocusedStack=ActivityStack{976a693 stackId=0, 1 tasks}
mSleepTimeout=false
mCurTaskId=5
mUserStackInFront={}
mActivityContainers={0=ActivtyContainer{0}A}
mLockTaskModeState=NONE mLockTaskPackages (userId:packages)=
0:[]
mLockTaskModeTasks[]

Activity具体项

以下列出执行”adb shell dumpsys activity a” 命令之后打印的ActivityRecord信息,具体源码我这边不再列举,可以自己去看以下代码:

frameworks/base/services/core/java/com/android/server/am/ActivityRecord.dump()

为了避免内容杂乱,只列举出一些常用的ActivityRecord属性以及对应的大概意义,在平时的DEBUG中方便查看:

成员变量 意义
packageName 应用包名
processName 应用进程名
launchedFromPackage 从xxx进程启动
userId 用户id,用来在不同空间中发挥作用,比如分身空间、双开空间
app 所属进程
Intent 与Activity关联的Intent
frontOfTask 当前Task的根Activity
task 所属Task
taskAffinity 所在Task名
realActivity 对应的Activity
baseDir apk所在目录
resDir 资源所在目录
dataDir 数据所在目录
mActivityType Activity类型
labelRes 指定的label属性
icon 应用icon drawable id
theme 应用theme资源 id
resultTo 回调的Activity
resultWho resultTo可以用到的额外识别符
resultCode 从resultTo传入的识别code
taskDescription Activity在最近任务中的信息
iconFilename icon的文件名
label 当前Task的描述
color primary color属性
state 所在的生命周期
stopped 是否是经过onStop周期
finishing 进入或已经finish
inHistory 是否在历史列表中保存
visible 是否可见
idle 是否进入idle状态
fullscreen 是否覆盖全屏
noDisplay 是否显示
launchMode 启动模式; 取值范围从0-3依次是standard, singleTop, singleTask, singleInstance
mActivityType Activity的类型,从0-2依次是普通,Home,最近任务的Activity
displayStartTime 此次Activity被启动的时间
startTime 上一次这类Activity被启动的时间
connections Activity与Service的绑定情况

结构图

查看大图

dumpsys activity r/all/top 结构图

dumpsys activity all && dumpsys activity top

输入adb shell dumpsys activity all adb 将会输出所有Activity及其Fragment、View的层级信息,十分详细。这条命令的原理在于AMS会binder call到每个正在运行Activity的载体进程应用中,打印出详细的每个单项Activity的信息。adb shell dumpss activity top只会输出当前在前台的Activity信息,信息量不会那么大。因为这里信息能够具体到Fragment和View,调试的时候很有帮助。结构图的作用在于对输出的信息有个大概的了解和解决问题时的定位。

查看大图

dumpsys activity r

此命令是以最新任务的视角,可以输出RecentTask中的信息 (可以在文章最开头的图中找到它的位置),它保存着最近任务抽屉中的Task信息

查看大图

ActivityStack/ActivityRecord/TaskRecord等等

以下是这几个与launchMode密切相关类的toString输出,因为比较简短,常常会出现在logcat日志中,熟悉它们的信息有利于解决Activity相关的问题

frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor$ActivityDisplay

1
2
3
4
@Override
public String toString() {
return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
}

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

1
2
3
4
5
@Override
public String toString() {
return "ActivityStack{" + Integer.toHexString(System.identityHashCode(this))
+ " stackId=" + mStackId + ", " + mTaskHistory.size() + " tasks}";
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Override
public String toString() {
StringBuilder sb = new StringBuilder(128);
if (stringName != null) {
sb.append(stringName);
sb.append(" U=");
sb.append(userId);
sb.append(" sz=");
sb.append(mActivities.size());
sb.append('}');
return sb.toString();
}
sb.append("TaskRecord{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" #");
sb.append(taskId);
if (affinity != null) {
sb.append(" A=");
sb.append(affinity);
} else if (intent != null) {
sb.append(" I=");
sb.append(intent.getComponent().flattenToShortString());
} else if (affinityIntent != null) {
sb.append(" aI=");
sb.append(affinityIntent.getComponent().flattenToShortString());
} else {
sb.append(" ??");
}
stringName = sb.toString();
return toString();
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public String toString() {
if (stringName != null) {
return stringName + " t" + (task == null ? INVALID_TASK_ID : task.taskId) +
(finishing ? " f}" : "}");
}
StringBuilder sb = new StringBuilder(128);
sb.append("ActivityRecord{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" u");
sb.append(userId);
sb.append(' ');
sb.append(intent.getComponent().flattenToShortString());
stringName = sb.toString();
return toString();
}

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

1
2
3
4
5
6
7
8
9
10
11
12
public String toString() {
if (stringName != null) {
return stringName;
}
StringBuilder sb = new StringBuilder(128);
sb.append("ProcessRecord{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(' ');
toShortString(sb);
sb.append('}');
return stringName = sb.toString();
}

总结

本篇文章中我以launchMode为主线,Activity Stack/Task为主题,深度分析了Stack和Task是如何在启动不同模式的Activity中被运用的。关于这块的规则大家看看launchMode章节的四张图就好,我就不累述了。

文章的最后,列出Activity的管理结构,和文章最初的图片对应:

  • ActivityStackSupervisor: ActivityStack的大管家,直接掌管了所有的ActivityDisplay, 同时直接引用了一些常用的Stack和Activity
  • ActivityDisplay: Activity视图,和分屏功能有关,普通开发者基本察觉不到它
  • ActivityStack: Activity 栈,维护着多个Task
  • ActivityTask: Activity 任务,直接掌管着Activity,维护者多个Activity
  • ActivityRecord: 开发者最常接触到的Activity在AMS中的抽象
扫码支持0.99元,您的支持将鼓励我继续创作!