本文基于 android-14.0.0_r15 版本讲解
# 1. 任务(Task)的概念
在 Android 中,Task 是用户在执行某项工作时与之互动的一系列 Activity 的集合。
更通俗地说,一个具体的事情可以被看作是一个任务(Task),比如:
- 打开微信支付二维码付款
- 打开抖音刷短视频
- 打开拼多多网购
一个 Task 又可以被细分成多个子步骤,每个子步骤就是一个 Activity。这个 Activity 既可以是当前 App 中的 Activity,也可以是其他 App 中的的 Activity。比如你在微信群收到了一个 markdown 文件,微信本身是不支持读写 markdown 文件的,当你点击这个文件时,微信会弹出一个小窗口让你选择其他 App 打开这个 markdown 文件。
当我们点击任意一个图标后,就进入到其他 App 的 Activity 中了。
这种基于组件的设计,弱化了进程的概念,通过重用 Activity 可以节省一定的开销,同时也为用户提供了一致的界面和用户体验。即使有很多个 Activity 分别都是来自于不同的 App,Android 系统仍然可以将它们无缝地结合到一起。
# 2. 内部数据结构分析
那么 Android 是怎么实现的呢?我们接下来通过 Android 内部相关数据结构的解析来了解一二。
# 2.1 ActivityRecord
在 Android 系统的 SystemServer 端,使用 ActivityRecord 对象来记录一个 Activity 相关的信息:
/**
* An entry in the history task, representing an activity.
*/
final class ActivityRecord extends WindowToken implements WindowManagerService.AppFreezeListener {
// 基本配置信息
final ActivityInfo info; // AndroidManifest.xml 中提供的信息
final String packageName; // the package implementing intent's component
final String processName; // process where this component wants to run
final String taskAffinity; // as per ActivityInfo.taskAffinity
private int labelRes; // the label information from the package mgr.
private int icon; // resource identifier of activity's icon.
private int logo; // resource identifier of activity's logo.
private int theme; // resource identifier of activity's theme.
// ......
// Activity 启动时确定的信息
final int launchedFromPid; // always the pid who started the activity.
final int launchedFromUid; // always the uid who started the activity.
final int mUserId; // Which user is this running for?
final Intent intent; // the original intent that generated us
final ComponentName mActivityComponent; // the intent component, or target of an alias.
final String processName; // process where this component wants to run
//......
// 运行状态信息
private State mState; // current state we are in
// enum State {
// INITIALIZING,
// STARTED,
// RESUMED,
// PAUSING,
// PAUSED,
// STOPPING,
// STOPPED,
// FINISHING,
// DESTROYING,
// DESTROYED,
// RESTARTING_PROCESS
// }
boolean idle; // has the activity gone idle?
boolean finishing; // activity in pending finish list?
// 管理对象
// 管理 Activity 的 ATMS 对象
final ActivityTaskManagerService mAtmService;// owner
// 这个对象用于在多个进程中标识 Activity,后面会单独来讲
final IApplicationToken.Stub appToken;// window manager token
// activity 所在的 task
private Task task; // the task this is in.
// .....
}
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
ActivityRecord 的父类 WindowToken 中有一个非常重要的成员 token:
// frameworks/base/services/core/java/com/android/server/wm/WindowToken.java
class WindowToken extends WindowContainer<WindowState> {
/** The actual token */
// 在 SystemServer 中 token 继承自 IBinder,是一个 Binder 服务端对象,用于在多个进程中索引 ActivityRecord 对象
final IBinder token;
// ......
}
2
3
4
5
6
7
8
ActivityRecord 对象中主要记录了五类信息:
- 配置信息:这些配置信息大多来自应用中的
AndroidManifest.xml
,比如:属于哪个 Package,所在的进程名称、任务名、组件类名、logo、主题等等,这些信息基本上是固定的 - 启动时基本信息:启动 Activity 时,才确定的信息,在每次启动时可能会不同,如 launchedFromPid、task、launchedFromPackage、processName 等
- 运行状态信息:记录 activity 当前状态的信息,比如 mState、idle、stopped、finishing 等
- 管理对象:管理当前 ActivityRecord 的对象
- token:在 SystemServer 中 token 继承自 IBinder,是一个 Binder 服务端对象,用于在多个进程中索引 ActivityRecord 对象
# 2.2 Task
在 Android14 系统源码中,使用 Task 对象来描述一个任务(Task):
class Task extends TaskFragment {
// ......
// task 的 affinity 属性
String affinity; // The affinity name for this task, or null; may change identity.
// task 的初始 affinity 属性,也就是添加第一个 ActivityRecord 时,使用 ActivityRecord 的 affinity 初始化 task 的 affinity
String rootAffinity; // Initial base affinity, or null; does not change from initial root.
// task 的 id
final int mTaskId;
// ......
}
2
3
4
5
6
7
8
9
10
11
12
13
- affinity:用于指定 Task 的 affinity 属性,运行过程中可能改变
- rootAffinity:task 的初始 affinity 属性,也就是添加第一个 ActivityRecord 时,使用 ActivityRecord 的 affinity 初始化 task 的 affinity,运行过程中不改变
- final int mTaskId:task 的 id
我们接着看看 Task 的父类 TaskFragment:
// task 的父类 TaskFragment
class TaskFragment extends WindowContainer<WindowContainer> {
// Task 相关的管理对象
final ActivityTaskManagerService mAtmService;
final ActivityTaskSupervisor mTaskSupervisor;
final RootWindowContainer mRootWindowContainer;
private final TaskFragmentOrganizerController mTaskFragmentOrganizerController;
// Task 中几个重要的 ActivityRecord 对象
private ActivityRecord mPausingActivity = null;
ActivityRecord mLastPausedActivity = null;
private ActivityRecord mResumedActivity = null;
// ......
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
主要两部分内容:
- Task 相关的管理对象
- Task 中几个重要的 ActivityRecord 对
- mPausingActivity 正在 pause 的 Acitivity
- mLastPausedActivity 已经 pause 的 Activity
我们再看 TaskFragment 的父类:
// TaskFragment 的 父类
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable,
InsetsControlTarget {
// task 的父节点,可以是 TaskDisplayArea 或者 Task
private WindowContainer<WindowContainer> mParent = null;
// task 的子节点,可以是 Task 或者 ActivityRecord
protected final WindowList<E> mChildren = new WindowList<E>();
// ......
}
2
3
4
5
6
7
8
9
10
11
12
- mParent 用于指定 Task 的父节点,可以是 TaskDisplayArea 或者 Task
- mChildren 用于指定 Task 的子节点,可以是 Task 或者 ActivityRecord
# 3. 使用命令行分析内部数据结构
# 3.1 Launcher
模拟器启动后,处于 Launcher 时,运行命令:
adb shell dumpsys activity containers
打印的内容很多,我们找到 task 相关的内容:
#1 DefaultTaskDisplayArea type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][0,0] bounds=[0,0][720,1280]
#1 Task=1 type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1280]
#0 Task=7 type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1280]
#0 ActivityRecord{5389581 u0 com.android.launcher3/.uioverrides.QuickstepLauncher t7} type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1280]
#0 eec3374 com.android.launcher3/com.android.launcher3.uioverrides.QuickstepLauncher type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1280]
#0 Task=2 type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][0,0] bounds=[0,0][720,1280]
#1 Task=4 type=undefined mode=multi-window override-mode=multi-window requested-bounds=[0,1280][720,1920] bounds=[0,1280][720,1920]
#0 Task=3 type=undefined mode=multi-window override-mode=multi-window requested-bounds=[0,0][0,0] bounds=[0,0][720,1280]
2
3
4
5
6
7
8
将打印内容转换为图:
# 3.2 从 Launcher 启动一个 Demo App
接着我们准备一个空 Activity 的 Demo App,并安装到模拟器上。
接着打开这个 App,然后运行命令:
adb shell dumpsys activity containers
打印的内容很多,接着找到 task 相关的内容:
#1 DefaultTaskDisplayArea type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][0,0] bounds=[0,0][720,1280]
#2 Task=13 type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1280]
#0 ActivityRecord{fe72af2 u0 com.yuandaimai/.MainActivity t13} type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1280]
#0 47f0a52 com.yuandaimai/com.yuandaimai.MainActivity type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1280]
#1 Task=1 type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1280]
#0 Task=7 type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1280]
#0 ActivityRecord{5389581 u0 com.android.launcher3/.uioverrides.QuickstepLauncher t7} type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1280]
#0 cc62df0 com.android.launcher3/com.android.launcher3.uioverrides.QuickstepLauncher type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1280]
#0 Task=2 type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][0,0] bounds=[0,0][720,1280]
#1 Task=4 type=undefined mode=multi-window override-mode=multi-window requested-bounds=[0,1280][720,1920] bounds=[0,1280][720,1920]
#0 Task=3 type=undefined mode=multi-window override-mode=multi-window requested-bounds=[0,0][0,0] bounds=[0,0][720,1280]
2
3
4
5
6
7
8
9
10
11
将打印内容转换为图:
# 4. 总结
本节主要讲了如下内容
- Android 中 Task 的概念
- Task 相关数据结构的分析
- 使用命令行查看内部数据结构情况,并将文本内容转化为图