要理解 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 合成" 的流程:
- ViewRootImpl 触发 View 树绘制
前面提到的requestLayout()
会触发performTraversals()
,该方法会依次调用 DecorView 的measure()
(测量大小)、layout()
(确定位置)、draw()
(绘制内容)。绘制的结果(像素数据)会写入 WMS 分配的Surface中(Surface 是应用进程与 SurfaceFlinger 之间的共享内存缓冲区)。 - 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 合成显示。