WindowManager 添加 DecorView 的本质及显示原理

要理解 WindowManager 添加 DecorView 的本质及显示原理,需要从 Android 的窗口管理体系、跨进程通信机制和渲染流程三个维度,结合核心源码进行分析。以下是逐步拆解的过程:

一、DecorView 的本质:窗口的根视图

DecorView 是 Activity/Fragment 的顶层 View,本质是一个 FrameLayout,包含标题栏(如 ActionBar/Toolbar)和内容区(R.id.content)。它不是独立存在的,而是依附于PhoneWindow(Window 的唯一实现类):

java 复制代码
// PhoneWindow.java
private DecorView mDecor; // DecorView是PhoneWindow的内部成员

// 当Activity初始化时,会创建PhoneWindow并初始化DecorView
public void setContentView(int layoutResID) {
    if (mDecor == null) {
        mDecor = installDecor(); // 创建DecorView
    }
    // 将用户布局添加到DecorView的内容区
    mLayoutInflater.inflate(layoutResID, mContentParent);
}

DecorView 是窗口内容的载体,但它本身无法直接显示,必须通过WindowManager与系统服务交互,才能被绘制到屏幕上。

二、WindowManager 添加 DecorView 的流程:从应用进程到系统进程

WindowManager 是一个接口,其核心实现是WindowManagerImpl (应用进程内)和WindowManagerService(WMS) (系统进程,负责全局窗口管理)。添加 DecorView 的核心逻辑在WindowManager.addView(),具体流程如下:

1. 应用进程:WindowManagerImpl 的代理转发

Activity 通过getWindowManager()获取的是 WindowManagerImpl 实例,它本身不处理逻辑,而是委托给WindowManagerGlobal(全局单例,管理应用内所有窗口):

java 复制代码
// WindowManagerImpl.java
@Override
public void addView(View view, ViewGroup.LayoutParams params) {
    // 转发给WindowManagerGlobal处理
    mGlobal.addView(view, params, mDisplay, mParentWindow);
}

2. WindowManagerGlobal:创建 ViewRootImpl,关联 DecorView 与系统服务

WindowManagerGlobal 的addView()是关键步骤,主要做三件事:

  • 校验参数(如 DecorView 是否已添加);

  • 创建ViewRootImpl(View 树的管理者,连接 View 和 WMS 的桥梁);

  • 保存窗口信息(View、ViewRootImpl、LayoutParams)到列表管理。

源码如下:

java 复制代码
// WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
    // 1. 校验:DecorView必须是首次添加,且params必须是WindowManager.LayoutParams
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }
    if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }

    // 2. 创建ViewRootImpl(核心!)
    ViewRootImpl root = new ViewRootImpl(view.getContext(), display);
    // 设置DecorView的布局参数(窗口类型、宽高、flags等)
    view.setLayoutParams(wparams);

    // 3. 保存窗口信息到列表(管理应用内所有窗口)
    mViews.add(view); // 存储DecorView
    mRoots.add(root); // 存储对应的ViewRootImpl
    mParams.add(wparams); // 存储窗口参数

    // 4. 关键:通过ViewRootImpl将DecorView与WMS关联
    try {
        root.setView(view, wparams, panelParentView); // 触发与WMS的交互
    } catch (RuntimeException e) {
        // 异常处理:移除已添加的窗口信息
        removeViewLocked(index, true);
        throw e;
    }
}

3. ViewRootImpl:跨进程请求 WMS 添加窗口

ViewRootImpl 的setView()方法是连接应用进程与系统进程(WMS)的核心,主要做两件事:

  • 触发 View 树的首次布局(measure/layout/draw);

  • 通过IWindowSession(WMS 的客户端代理)向 WMS 发送 "添加窗口" 的请求。

源码关键逻辑:

java 复制代码
// ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            mView = view; // 关联DecorView

            // 1. 触发View树的布局流程(首次绘制)
            requestLayout(); // 最终会调用performTraversals(),执行measure/layout/draw

            // 2. 准备跨进程通信的参数:创建IWindow(应用进程的窗口代理,供WMS回调)
            final IWindow sessionWindow = new W(this);

            // 3. 跨进程调用WMS的addWindow()方法
            try {
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                // 通过IWindowSession与WMS通信
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
            } catch (RemoteException e) {
                // 异常处理
            }
        }
    }
}

这里的mWindowSession是 IWindowSession 类型,是应用进程与 WMS 通信的 Binder 接口(系统进程提供)。addToDisplay()最终会调用 WMS 的addWindow()方法。

4. 系统进程:WMS 管理窗口并分配绘制表面(Surface)

WMS 是系统进程(system_server)中的核心服务,负责管理所有窗口的层级、布局、显示权限等。其addWindow()方法主要逻辑:

  • 校验窗口参数(如类型、权限);
  • 为窗口分配Surface(绘图缓冲区,由 SurfaceFlinger 管理);
  • 将窗口加入全局窗口列表,计算 Z 轴顺序(层级)。
java 复制代码
// WindowManagerService.java
public int addWindow(Session session, IWindow client, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
        Rect outStableInsets, Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout,
        InputChannel outInputChannel) {
    // 1. 校验窗口合法性(如类型是否允许,权限是否足够)
    if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) {
        // 非应用窗口(如系统窗口)需要权限校验
        if (!mPolicy.checkAddPermission(attrs, appOp)) {
            throw new SecurityException("Permission denied");
        }
    }

    // 2. 创建WindowState(WMS中窗口的代表,记录窗口状态)
    final WindowState win = new WindowState(this, session, client, token, parentWindow,
            appOp, attrs, viewVisibility, displayContent);

    // 3. 为窗口分配Surface(通过SurfaceSession与SurfaceFlinger交互)
    win.attach(); // 内部会请求SurfaceFlinger创建Surface

    // 4. 将窗口加入全局管理列表,计算层级(Z-order)
    displayContent.getWindowAnimator().addWindow(win);
    mWindowMap.put(client.asBinder(), win); // 存入映射表,便于后续管理

    // 5. 触发窗口布局计算(确定窗口在屏幕上的位置)
    displayContent.layoutNeeded = true;
    return ADD_OKAY;
}

至此,DecorView 对应的窗口已被 WMS 识别,并分配了用于绘制的 Surface。

三、DecorView 能显示的核心:渲染流程

DecorView 最终能显示在屏幕上,依赖于 "View 绘制 → 数据写入 Surface → SurfaceFlinger 合成" 的流程:

  1. ViewRootImpl 触发 View 树绘制
    前面提到的requestLayout()会触发performTraversals(),该方法会依次调用 DecorView 的measure()(测量大小)、layout()(确定位置)、draw()(绘制内容)。绘制的结果(像素数据)会写入 WMS 分配的Surface中(Surface 是应用进程与 SurfaceFlinger 之间的共享内存缓冲区)。
  2. SurfaceFlinger 合成显示
    SurfaceFlinger 是另一个系统进程,负责管理所有应用的 Surface,并将它们按照 WMS 计算的层级(Z-order)合成到屏幕的帧缓冲区(FrameBuffer)。最终,合成后的图像通过硬件显示到屏幕上。

四、总结:DecorView 到底 "添加" 到了哪里?

从物理载体看,DecorView 作为 View 树的根,其绘制内容被写入Surface (由 SurfaceFlinger 管理的绘图缓冲区);

从逻辑管理看,DecorView 对应的窗口信息被记录在 WMS 的WindowState中,纳入系统全局窗口管理;

从显示原理看,Surface 中的像素数据经 SurfaceFlinger 合成后,最终输出到屏幕的帧缓冲区,因此能被用户看到。

整个过程的核心是:应用进程通过 ViewRootImpl 与 WMS 跨进程通信,WMS 分配 Surface 并管理窗口层级,ViewRootImpl 负责将 DecorView 的内容绘制到 Surface,最后由 SurfaceFlinger 合成显示

相关推荐
xzkyd outpaper9 分钟前
Kotlin 协程启动方式
android·开发语言·kotlin
用户2018792831672 小时前
Activity 与 Service、BroadcastReceiver、ContentProvider中ANR 的差异
android
用户2018792831672 小时前
解密:DecorView到底添加到哪里去了,为何能显示出来?
android
Harry技术3 小时前
Trae搭建Android 开发中 MVVM 架构,使用指南:组件、步骤与最佳实践
android·kotlin·trae
悠哉清闲5 小时前
Room 数据存储
android·数据库
恋猫de小郭11 小时前
Flutter 3.35 发布,快来看看有什么更新吧
android·前端·flutter
雨白14 小时前
加密、签名与编码
android
李新_15 小时前
【Android Bug Fix】UI不响应、异位异常排查
android·程序员
帅得不敢出门16 小时前
Android Framework定制长按电源键关机的窗口
android·java·framework