源码视角下的 Android 开机流程(三):从 Launcher Task 到 WindowState 挂载

一、这篇接着解决什么问题

上一篇把 WMS 的基础骨架搭了起来:

text 复制代码
RootWindowContainer
└── DisplayContent
    └── Feature DisplayArea
        ├── DisplayArea.Tokens
        ├── DefaultTaskDisplayArea
        └── ImeContainer

这棵树已经能接住 Task 和窗口,但此时还没有 Launcher 对应的 TaskActivityRecordWindowState

这一篇只追一条线:Launcher 被解析为 HOME Activity 后,系统怎样把它放进 DefaultTaskDisplayArea,又怎样接住 Launcher 进程发回来的窗口请求。最终补齐的层级如下:

text 复制代码
DefaultTaskDisplayArea
└── Root Home Task
    └── Launcher Task
        └── ActivityRecord(同时也是 WindowToken)
            └── WindowState

本文以 Android 14 为准,代码均做了裁剪。分析停在:

java 复制代码
win.mToken.addWindow(win);

执行到这里,只能说明 WindowState 已经挂进 WindowContainer 树。Launcher 的 Surface、首帧以及 BootAnimation 退出放到下一篇。

二、先把几个对象对上

2.1 它们不在同一进程,也不在同一时刻创建

对象 所在进程 创建时机
Root Home Task system_server RootWindowContainer.setWindowManager() 阶段
ActivityRecord system_server ActivityStarter 解析启动请求时
Launcher Task system_server 首次启动 HOME、没有可复用 Task 时
PhoneWindow Launcher 进程 Activity.attach()
DecorView Launcher 进程 PhoneWindow.installDecor()
ViewRootImpl Launcher 进程 WindowManagerGlobal.addView()
WindowState system_server WindowManagerService.addWindow()

顺序上,Task 和 ActivityRecord 先在 system_server 中入树。Launcher Activity 执行到 Resume 后,应用进程才向 WMS 添加窗口,WindowState 是这时创建的。

2.2 ActivityRecord 为什么能成为 WindowState 的父节点

ActivityRecord 的定义在 frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java

java 复制代码
final class ActivityRecord extends WindowToken
        implements WindowManagerService.AppFreezeListener {
    ...
}

它不是单纯的 Activity 状态记录。对 WMS 来说,ActivityRecord 本身就是应用窗口的 WindowToken

构造 ActivityRecord 时还会创建一枚 Binder Token:

java 复制代码
private ActivityRecord(...) {
    super(_service.mWindowManager,
            new Token(),
            TYPE_APPLICATION,
            true,
            null /* displayContent */,
            false /* ownerCanManageAppTokens */);
    ...
}

这里需要区分:

  • ActivityRecordsystem_server 中的 WindowContainer 节点;
  • ActivityRecord.token 是跨进程使用的 Binder 标识。

后面的窗口添加流程,本质上是在传递并找回这枚 Token:

text 复制代码
ActivityRecord.token
→ ClientTransaction
→ Activity.attach(token)
→ PhoneWindow.mAppToken
→ WindowManager.LayoutParams.token
→ WindowManagerService.addWindow()
→ ActivityRecord

抓住这条线,后面的类虽然多,关系并不乱。

三、system_server 先建立 Launcher 的 Activity 层级

3.1 Root Home Task 已经提前创建

Root Home Task 不是启动 Launcher 时才临时创建的。RootWindowContainer.setWindowManager() 创建完 DisplayContentDefaultTaskDisplayArea 后,会立即保证默认显示区域存在一个 HOME 类型的根 Task。

源码:

  • frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
  • frameworks/base/services/core/java/com/android/server/wm/TaskDisplayArea.java
java 复制代码
// RootWindowContainer.setWindowManager()
final TaskDisplayArea defaultTaskDisplayArea =
        getDefaultTaskDisplayArea();
defaultTaskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
java 复制代码
Task getOrCreateRootHomeTask(boolean onTop) {
    Task homeTask = getRootHomeTask();
    if (homeTask == null && canHostHomeTask()) {
        homeTask = createRootTask(
                WINDOWING_MODE_UNDEFINED,
                ACTIVITY_TYPE_HOME,
                onTop);
    }
    return homeTask;
}

这个 Root Home Task 此时还是空的。它是 HOME 的根容器,不是最终承载 Launcher Activity 的叶子 Task。

3.2 startHomeActivity 创建 ActivityRecord

上一篇已经走到 AMS.systemReady() 解析 HOME Activity。解析出 Launcher 后,调用链进入:

text 复制代码
RootWindowContainer.startHomeOnTaskDisplayArea()
→ ActivityStartController.startHomeActivity()
→ ActivityStarter.execute()

ActivityStartController.startHomeActivity() 在启动参数里指定 HOME 类型和目标 TaskDisplayArea

java 复制代码
options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
options.setLaunchTaskDisplayArea(
        taskDisplayArea.mRemoteToken
                .toWindowContainerToken());

taskDisplayArea.getOrCreateRootHomeTask(ON_TOP);

obtainStarter(intent, "startHomeActivity: " + reason)
        .setActivityInfo(aInfo)
        .setActivityOptions(options.toBundle())
        .execute();

源码:frameworks/base/services/core/java/com/android/server/wm/ActivityStartController.java

这里真正影响后续 Task 选择的是两个约束:这次启动属于 ACTIVITY_TYPE_HOME,目标区域是当前的 TaskDisplayArea

execute() 随后进入 ActivityStarter.executeRequest()。完成 Intent、权限和 ActivityInfo 检查后,系统先创建 Launcher 的 ActivityRecord

java 复制代码
final ActivityRecord r =
        new ActivityRecord.Builder(mService)
                .setCaller(callerApp)
                .setIntent(intent)
                .setActivityInfo(aInfo)
                .setActivityOptions(checkedOptions)
                .setSourceRecord(sourceRecord)
                .build();

源码:frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java

现在只有 ActivityRecord 对象,还没有父节点。

3.3 Root Home Task 下面为什么还有一个 Launcher Task

ActivityStarter.startActivityInner() 先确定目标根 Task。首次启动 Launcher、没有可复用 Task 时,mTargetRootTask 就是前面创建的 Root Home Task:

java 复制代码
if (mTargetRootTask == null) {
    mTargetRootTask = getOrCreateRootTask(
            mStartActivity,
            mLaunchFlags,
            targetTask,
            mOptions);
}

if (newTask) {
    setNewTask(taskToAffiliate);
}

setNewTask() 再调用 Root Home Task 的 reuseOrCreateTask()

java 复制代码
final Task task = mTargetRootTask.reuseOrCreateTask(
        mStartActivity.info,
        mIntent,
        mVoiceSession,
        mVoiceInteractor,
        toTop,
        mStartActivity,
        mSourceRecord,
        mOptions);

addOrReparentStartingActivity(
        task, "setTaskFromReuseOrCreateNewTask");

Task.reuseOrCreateTask() 的分支很直接:当前 Task 能作为叶子复用,就直接使用;不能复用,就创建一个子 Task。

java 复制代码
if (canReuseAsLeafTask()) {
    task = reuseAsLeafTask(...);
} else {
    task = new Task.Builder(mAtmService)
            .setTaskId(taskId)
            .setActivityInfo(info)
            .setIntent(intent)
            .setParent(this)
            .build();
}

源码:frameworks/base/services/core/java/com/android/server/wm/Task.java

在 Android 14 默认实现中,HOME 类型的根 Task 不满足 canReuseAsLeafTask() 的条件,所以 this,也就是 Root Home Task,会多出一个子 Task:

text 复制代码
DefaultTaskDisplayArea
└── Root Home Task
    └── Launcher Task

厂商分支可能调整 Task 组织方式,实际设备仍应以 dumpsys activity containers 为准。

3.4 ActivityRecord 挂到 Launcher Task

Task 确定后,addOrReparentStartingActivity() 才把 ActivityRecord 放进去:

java 复制代码
if (mStartActivity.getTaskFragment() == null
        || mStartActivity.getTaskFragment() == newParent) {
    newParent.addChild(mStartActivity, POSITION_TOP);
} else {
    mStartActivity.reparent(
            newParent,
            newParent.getChildCount(),
            reason);
}

普通 Launcher 场景没有嵌入式 TaskFragmentnewParent 就是 Launcher Task:

text 复制代码
DefaultTaskDisplayArea
└── Root Home Task
    └── Launcher Task
        └── ActivityRecord

ActivityRecord 入树时会感知自己所在的 DisplayContent,随后走到:

text 复制代码
ActivityRecord.onDisplayChanged()
→ WindowToken.onDisplayChanged()
→ DisplayContent.reParentWindowToken()
→ DisplayContent.addWindowToken()

DisplayContent.addWindowToken() 先登记 Binder Token:

java 复制代码
mTokenMap.put(binder, token);

if (token.asActivityRecord() == null) {
    final DisplayArea.Tokens da =
            findAreaForToken(token).asTokens();
    da.addChild(token);
}

源码:frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

这里没有把 ActivityRecord 再挂到 DisplayArea.Tokens。原因很简单:它已经在 Launcher Task 下面,WMS 只需把 ActivityRecord.token 和对象登记到 mTokenMapif 中的挂载逻辑留给没有 Task 父节点的非应用 WindowToken

到这一步,Launcher 的 Activity 层级已经建立,应用进程还没有创建窗口。

四、Activity Token 传到 Launcher 进程

4.1 realStartActivityLocked 下发启动事务

Launcher 进程不存在时,ATMS 会先通过 AMS 请求 Zygote 创建进程。进程完成 Binder attach 后,ActivityTaskSupervisor.realStartActivityLocked()ActivityRecord 与进程关联,并下发 ClientTransaction

源码:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java

java 复制代码
r.setProcess(proc);

final ClientTransaction clientTransaction =
        ClientTransaction.obtain(
                proc.getThread(), r.token);

clientTransaction.addCallback(
        LaunchActivityItem.obtain(
                new Intent(r.intent),
                System.identityHashCode(r),
                r.info,
                ...));

clientTransaction.setLifecycleStateRequest(
        ResumeActivityItem.obtain(...));

mService.getLifecycleManager()
        .scheduleTransaction(clientTransaction);

r.token 跟着事务跨进程进入 Launcher。LaunchActivityItem 创建 Activity,最终状态请求则把 Activity 推进到 Resume:

text 复制代码
LaunchActivityItem.execute()
→ ActivityThread.handleLaunchActivity()

ResumeActivityItem.execute()
→ ActivityThread.handleResumeActivity()

4.2 Activity.attach 把 Token 交给 PhoneWindow

ActivityThread.performLaunchActivity() 反射创建 Launcher Activity,然后调用 attach()

java 复制代码
Activity activity = mInstrumentation.newActivity(
        classLoader,
        component.getClassName(),
        r.intent);

activity.attach(
        appContext,
        this,
        getInstrumentation(),
        r.token,
        ...);

mInstrumentation.callActivityOnCreate(
        activity, r.state);

源码:frameworks/base/core/java/android/app/ActivityThread.java

Activity.attach() 创建 PhoneWindow,并把收到的 Token 传给它:

java 复制代码
mWindow = new PhoneWindow(
        this, window, activityConfigCallback);

mToken = token;

mWindow.setWindowManager(
        (WindowManager) context.getSystemService(
                Context.WINDOW_SERVICE),
        mToken,
        mComponent.flattenToString(),
        (info.flags
                & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);

mWindowManager = mWindow.getWindowManager();

源码:frameworks/base/core/java/android/app/Activity.java

PhoneWindow 此时已经持有 Activity Token。随后 Launcher 在 onCreate() 中调用 setContentView()PhoneWindow.installDecor() 创建 DecorView 并填充布局。

源码:frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

java 复制代码
public void setContentView(int layoutResID) {
    if (mContentParent == null) {
        installDecor();
    }

    mLayoutInflater.inflate(
            layoutResID, mContentParent);
    ...
}

View 树已经有了,WMS 侧还没有 WindowState

4.3 onResume 之后才发起 addView

ResumeActivityItem 最终进入 ActivityThread.handleResumeActivity()onResume() 执行完,DecorView 才被交给 WindowManager:

java 复制代码
if (r.window == null
        && !a.mFinished
        && willBeVisible) {
    r.window = r.activity.getWindow();
    View decor = r.window.getDecorView();
    ViewManager wm = a.getWindowManager();
    WindowManager.LayoutParams l =
            r.window.getAttributes();

    l.type = WindowManager.LayoutParams
            .TYPE_BASE_APPLICATION;

    if (a.mVisibleFromClient
            && !a.mWindowAdded) {
        a.mWindowAdded = true;
        wm.addView(decor, l);
    }
}

这几个时点不要混在一起:onCreate() 结束时没有 WindowStateonResume() 结束后才开始 addView()addView() 返回也不等于首帧已经显示。

五、Activity Token 再回到 WMS

5.1 addView 创建 ViewRootImpl

客户端的窗口添加主线是:

text 复制代码
WindowManagerImpl.addView()
→ WindowManagerGlobal.addView()
→ new ViewRootImpl()
→ ViewRootImpl.setView()

WindowManagerImpl 先补充窗口参数,再交给进程内唯一的 WindowManagerGlobal

java 复制代码
public void addView(
        View view,
        ViewGroup.LayoutParams params) {
    applyTokens(params);
    mGlobal.addView(
            view,
            params,
            mContext.getDisplayNoVerify(),
            mParentWindow,
            mContext.getUserId());
}

源码:frameworks/base/core/java/android/view/WindowManagerImpl.java

WindowManagerGlobal.addView()DecorView 创建 ViewRootImpl

java 复制代码
root = new ViewRootImpl(
        view.getContext(), display);

mViews.add(view);
mRoots.add(root);
mParams.add(wparams);

root.setView(
        view, wparams, panelParentView, userId);

源码:frameworks/base/core/java/android/view/WindowManagerGlobal.java

WindowManagerGlobalViewRootImpl 都在 Launcher 进程中,不是 WindowContainer 树上的节点。

5.2 LayoutParams.token 从哪里来

Activity.attach() 传给 PhoneWindow 的 Activity Token,最终会被写入 WindowManager.LayoutParams.token

这一步发生在 Window.adjustLayoutParamsForSubWindow()

java 复制代码
if (wp.token == null) {
    wp.token = mContainer == null
            ? mAppToken
            : mContainer.mAppToken;
}

源码:frameworks/base/core/java/android/view/Window.java

Launcher 主窗口没有上级 Window,所以这里取的是 PhoneWindow.mAppToken。它就是最初的 ActivityRecord.token

5.3 IWindowSession 把请求送回 system_server

ViewRootImpl.setView() 通过 IWindowSession 添加窗口:

java 复制代码
res = mWindowSession.addToDisplayAsUser(
        mWindow,
        mWindowAttributes,
        getHostVisibility(),
        mDisplay.getDisplayId(),
        userId,
        mInsetsController
                .getRequestedVisibleTypes(),
        inputChannel,
        ...);

源码:frameworks/base/core/java/android/view/ViewRootImpl.java

mWindow 是应用侧的 IWindow Binder 对象,mWindowAttributes.token 是 Activity Token。服务端的 Session 只做一次转发:

java 复制代码
public int addToDisplayAsUser(
        IWindow window,
        WindowManager.LayoutParams attrs,
        ...) {
    return mService.addWindow(
            this, window, attrs, ...);
}

源码:frameworks/base/services/core/java/com/android/server/wm/Session.java

调用重新回到 system_server,目标是 WindowManagerService.addWindow()

六、WMS 把 WindowState 挂到 ActivityRecord

6.1 先用 attrs.token 找回 ActivityRecord

WindowManagerService.addWindow() 根据 LayoutParams.token 查询 DisplayContent.mTokenMap

java 复制代码
WindowToken token = displayContent.getWindowToken(
        hasParent
                ? parentWindow.mAttrs.token
                : attrs.token);

if (rootType >= FIRST_APPLICATION_WINDOW
        && rootType <= LAST_APPLICATION_WINDOW) {
    activity = token.asActivityRecord();
    if (activity == null) {
        return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
    }
}

源码:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

第三节已经把映射写入 mTokenMap

text 复制代码
ActivityRecord.token → ActivityRecord

所以 WMS 能根据 attrs.token 找回原来的 ActivityRecord。应用窗口如果携带未知 Token,或者 Token 对应的不是 ActivityRecord,会在这里被拒绝。

6.2 创建 WindowState,然后 addChild

Token 和窗口类型校验通过后,WMS 创建 WindowState

java 复制代码
final WindowState win = new WindowState(
        this,
        session,
        client,
        token,
        parentWindow,
        appOp[0],
        attrs,
        viewVisibility,
        session.mUid,
        userId,
        session.mCanAddInternalSystemWindow);

这里的 client 是 Launcher 进程中的 IWindowtoken 已经被解析为 Launcher 的 ActivityRecordWindowState 创建完成后,WMS 执行:

java 复制代码
win.attach();
mWindowMap.put(client.asBinder(), win);
...
win.mToken.addWindow(win);

对于 Launcher 主窗口:

java 复制代码
win.mToken == ActivityRecord

ActivityRecord 继承了 WindowToken.addWindow()

java 复制代码
void addWindow(final WindowState win) {
    if (win.isChildWindow()) {
        return;
    }

    ...

    if (!mChildren.contains(win)) {
        addChild(win, mWindowComparator);
        mWmService.mWindowsChanged = true;
    }
}

源码:frameworks/base/services/core/java/com/android/server/wm/WindowToken.java

addChild() 执行后,Launcher 窗口进入正式层级:

text 复制代码
DefaultTaskDisplayArea
└── Root Home Task
    └── Launcher Task
        └── ActivityRecord
            └── WindowState

现在 WMS 已经知道这个窗口属于哪个 Activity、Task 和 DisplayArea。至于窗口的大小、Surface 以及画面何时可见,还要继续走下一篇的流程。

七、顺便对照一下系统窗口

Launcher 的 WindowState 挂在 ActivityRecord 下。非应用窗口没有 Activity 和 Task,它们的父节点通常是第二篇创建的 DisplayArea.Tokens

text 复制代码
DisplayArea.Tokens
└── WindowToken
    └── WindowState

非应用 WindowToken 有两种创建方式,容易混淆的地方就在这里。

创建方式 典型窗口 入口
预先注册 Wallpaper、IME、VoiceInteraction、Accessibility Overlay、QS Dialog 系统服务先调用 addWindowToken(),再把 Token 交给窗口进程
动态创建 TYPE_APPLICATION_OVERLAY 客户端直接 addView(),WMS 在 addWindow() 中创建 Token

7.1 Wallpaper:先注册,再添加窗口

Wallpaper 连接建立时,WallpaperManagerService 先登记 Token,然后把同一枚 Token 传给 Wallpaper 进程:

java 复制代码
mWindowManagerInternal.addWindowToken(
        mToken,
        TYPE_WALLPAPER,
        mDisplayId,
        null /* options */);

connection.mService.attach(
        connection,
        mToken,
        TYPE_WALLPAPER,
        ...);

源码:frameworks/base/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java

WMS 创建 WallpaperWindowToken,构造过程最终进入 DisplayContent.addWindowToken()

java 复制代码
mTokenMap.put(binder, token);

if (token.asActivityRecord() == null) {
    final DisplayArea.Tokens da =
            findAreaForToken(token).asTokens();
    da.addChild(token);
}

Wallpaper 进程后续通过 LayoutParams.token 带回这枚 Token,WMS 找到已有的 WallpaperWindowToken,再把 WindowState 加进去。

7.2 Application Overlay:通常不需要预注册

系统应用使用标准 API 添加 TYPE_APPLICATION_OVERLAY 时,一般直接调用:

java 复制代码
params.type = WindowManager.LayoutParams
        .TYPE_APPLICATION_OVERLAY;
windowManager.addView(view, params);

如果 attrs.token 没有对应的 WindowToken,并且窗口类型允许动态创建,WindowManagerService.addWindow() 会补建一个:

java 复制代码
if (token == null) {
    if (!unprivilegedAppCanCreateTokenWith(...)) {
        return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
    }

    final IBinder binder = attrs.token != null
            ? attrs.token
            : client.asBinder();

    token = new WindowToken.Builder(
            this, binder, type)
            .setDisplayContent(displayContent)
            .setOwnerCanManageAppTokens(
                    session.mCanAddInternalSystemWindow)
            .build();
}

这条路径不会先经过 IWindowManager.addWindowToken()

text 复制代码
WindowManager.addView()
→ WindowManagerService.addWindow()
→ 创建 WindowToken
→ DisplayArea.Tokens.addChild(WindowToken)
→ WindowToken.addChild(WindowState)

系统应用身份只影响权限检查。能否主动调用隐藏的 IWindowManager.addWindowToken(),仍取决于 UID、签名和 MANAGE_APP_TOKENS 权限。

IME、Wallpaper、VoiceInteraction、Accessibility Overlay、QS Dialog 等类型更严格。它们使用未知或类型不匹配的 Token 时,addWindow() 会返回 ADD_BAD_APP_TOKEN 一类错误。

应用窗口和非应用窗口的区别可以收成一张表:

对比项 Launcher 应用窗口 非应用窗口
Token 容器 ActivityRecord 普通 WindowToken 或专用子类
Token 的父节点 Launcher Task 对应 Layer 的 DisplayArea.Tokens
Token 创建方式 Activity 启动时创建 ActivityRecord 预先注册或由 addWindow() 动态创建
最终结构 Task → ActivityRecord → WindowState DisplayArea.Tokens → WindowToken → WindowState

八、如何验证

8.1 查看 Task 和 ActivityRecord

shell 复制代码
adb shell dumpsys activity containers

在输出中搜索 Launcher 包名或 Activity 名称,确认它所在的 TaskDisplayArea、Root Home Task、Launcher Task 和 ActivityRecord

8.2 查看 WindowToken 和 WindowState

shell 复制代码
adb shell dumpsys window windows

Launcher 主窗口通常能看到:

  • mAttrs.type=TYPE_BASE_APPLICATION
  • 对应的 WindowTokenWindowState
  • mToken 与 Launcher Activity 的关联。

九、关键源码位置

环节 源码文件 核心方法
创建 Root Home Task frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java setWindowManager()
获取 Root Home Task frameworks/base/services/core/java/com/android/server/wm/TaskDisplayArea.java getOrCreateRootHomeTask()
发起 HOME 启动 frameworks/base/services/core/java/com/android/server/wm/ActivityStartController.java startHomeActivity()
创建、挂载 ActivityRecord frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java executeRequest()startActivityInner()addOrReparentStartingActivity()
创建 Launcher Task frameworks/base/services/core/java/com/android/server/wm/Task.java reuseOrCreateTask()
下发启动事务 frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java realStartActivityLocked()
创建 Launcher Activity frameworks/base/core/java/android/app/ActivityThread.java handleLaunchActivity()performLaunchActivity()
创建 PhoneWindow frameworks/base/core/java/android/app/Activity.java attach()
创建 DecorView frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java setContentView()installDecor()
发起 addView() frameworks/base/core/java/android/app/ActivityThread.java handleResumeActivity()
创建 ViewRootImpl frameworks/base/core/java/android/view/WindowManagerGlobal.java addView()
请求 WMS 添加窗口 frameworks/base/core/java/android/view/ViewRootImpl.java setView()
Binder 服务端转发 frameworks/base/services/core/java/com/android/server/wm/Session.java addToDisplayAsUser()
创建并挂载 WindowState frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java addWindow()
将窗口挂到 Token frameworks/base/services/core/java/com/android/server/wm/WindowToken.java addWindow()
非应用 Token 选择 DisplayArea frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java addWindowToken()findAreaForToken()

十、总结

实际上只做了两次挂载。

第一次发生在 Activity 启动阶段:Root Home Task 创建 Launcher Task,ActivityStarterActivityRecord 加到 Task 下。

第二次发生在窗口添加阶段:Launcher 进程把 Activity Token 放进 LayoutParams 送回 WMS,WMS 找回同一个 ActivityRecord,再把新建的 WindowState 加到它下面。

text 复制代码
DefaultTaskDisplayArea
└── Root Home Task
    └── Launcher Task
        └── ActivityRecord
            └── WindowState

现在树上的归属关系已经完整。下一篇从 ViewRootImpl 的首次 Traversal 和 relayoutWindow() 接着往下,分析 Launcher Surface、第一帧以及 BootAnimation 退出。

相关推荐
二流小码农1 小时前
鸿蒙开发:了解Context
android·ios·harmonyos
又见情义1 小时前
RK3568 Android 13 移植 EC20-CE 4G 模块全记录
android·arm开发·驱动开发
网安蟹佬霸1 小时前
Android安全攻防实战:从APK逆向到Frida动态Hook全流程详解(附脚本)
android·前端·安全·web安全·逆向·csrf·网安
雨声不在2 小时前
mitmdump Android 抓包
android
心平气和量大福大2 小时前
android-实例2-数据库sqlite(查询)
android·数据库·sqlite
时代分流2 小时前
云手机推荐2026:安卓全能、批量挂机、原生iOS全覆盖
android·ios·智能手机
恋猫de小郭2 小时前
Gradle 9.7.0 将提速 Android 构建,Sync 提升接近一倍
android·前端·flutter
Dovis(誓平步青云)2 小时前
心情日记的安全编辑流程:输入校验、删除确认与状态回收
android·运维·服务器·开发语言·windows·安全
宋拾壹2 小时前
php对接携程验票核销
android·php·android studio