Android14 中的任务与返回栈

2/29/2024

本文基于 android-14.0.0_r15 版本讲解

# 1. 任务(Task)的概念

在 Android 中,Task 是用户在执行某项工作时与之互动的一系列 Activity 的集合。

更通俗地说,一个具体的事情可以被看作是一个任务(Task),比如:

  • 打开微信支付二维码付款
  • 打开抖音刷短视频
  • 打开拼多多网购

一个 Task 又可以被细分成多个子步骤,每个子步骤就是一个 Activity。这个 Activity 既可以是当前 App 中的 Activity,也可以是其他 App 中的的 Activity。比如你在微信群收到了一个 markdown 文件,微信本身是不支持读写 markdown 文件的,当你点击这个文件时,微信会弹出一个小窗口让你选择其他 App 打开这个 markdown 文件。

20240110164554

当我们点击任意一个图标后,就进入到其他 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. 

    // .....
}
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

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;

    // ......
}
1
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;

    // ......
}
1
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;

    // ......
}
1
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>();
  
    // ......
}
1
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
1

打印的内容很多,我们找到 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]
1
2
3
4
5
6
7
8

将打印内容转换为图:

20240424150745

# 3.2 从 Launcher 启动一个 Demo App

接着我们准备一个空 Activity 的 Demo App,并安装到模拟器上。

接着打开这个 App,然后运行命令:

adb shell dumpsys activity containers
1

打印的内容很多,接着找到 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]
1
2
3
4
5
6
7
8
9
10
11

将打印内容转换为图:

20240424151328

# 4. 总结

本节主要讲了如下内容

  • Android 中 Task 的概念
  • Task 相关数据结构的分析
  • 使用命令行查看内部数据结构情况,并将文本内容转化为图

# 参考资料