一、从 Activity.setContentView() 开始的调用链
scss
Activity.setContentView(layoutId)
↓
PhoneWindow.setContentView(view)
↓
创建 / 装配 DecorView
↓
WindowManager.addView(DecorView, params)
↓
WindowManagerGlobal.addView(...)
↓
new ViewRootImpl(...)
↓
ViewRootImpl.setView(DecorView, params)
↓
requestLayout() → scheduleTraversals()
↓
doTraversal() → performMeasure() / performLayout() / performDraw()
↓
View 树 onDraw(Canvas)
↓
Canvas → Skia → (OpenGL ES / 软件渲染)
↓
绘制到 Surface 的 Buffer
二、Activity, Window, DecorView:界面"外壳结构"
Activity:逻辑入口 + 拥有一个 Window
- 每个 Activity 内部都持有一个 Window(通常是
PhoneWindow)。 - 当你调用:setContentView(R.layout.activity_main);
其实是
less
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID); // 交给 PhoneWindow
}
Window / PhoneWindow:管理 DecorView 的"窗口对象"
-
Window 是一个抽象类,PhoneWindow 是常见实现。
-
它负责:
- 创建并管理 DecorView(整个窗口的根视图);
- 处理标题栏、状态栏适配等"窗口装饰"。
简化的调用链:
scss
Activity.setContentView()
└─ PhoneWindow.setContentView()
├─ ensureDecor() // 确保 DecorView 已创建
│ └─ installDecor()
│ └─ mDecor = new DecorView(...)
└─ 将 layoutId inflate 成 View,并添加到 mDecor 的 content 区域
DecorView & View / ViewGroup:UI 树结构
-
DecorView:
- 继承自 ViewGroup;
- 是当前 Window 的 根 View;
- 内含状态栏/标题栏区域 + 内容区域。
-
XML 布局最终会成为:
scss
DecorView (FrameLayout)
└─ contentParent (id=android.R.id.content)
└─ 你的根 ViewGroup (例如 ConstraintLayout)
└─ 若干子 View / ViewGroup
这些 View/ViewGroup 负责在三大阶段中工作:
onMeasure():计算大小;onLayout():确定位置;onDraw(Canvas):在 Canvas 上画出自己。
这一切目前 还只是在内存结构上建立,还没有真正"出现在屏幕上"。
三、WindowManager / WMS / ViewRootImpl:把 DecorView 挂到窗口 & Surface 上
WindowManager:应用侧窗口管理接口
- Activity 内可以调用
getWindowManager()或使用WindowManager添加/更新 View(如 Dialog、Toast)。 - 当
PhoneWindow.setContentView()完成 DecorView 构造后,Activity/系统会调用:
ini
WindowManager wm = getWindowManager();
wm.addView(decorView, layoutParams);
WindowManagerGlobal / ViewRootImpl:建立与 WMS 的连接
WindowManager.addView() 内部最终走到:
scss
WindowManagerGlobal.addView(view, params)
├─ 创建 ViewRootImpl root = new ViewRootImpl(context, display)
├─ root.setView(view, params)
└─ 记录 (view ↔ root) 映射关系
此时几个关键点:
- ViewRootImpl 被创建;
- DecorView 被交给 ViewRootImpl 管理;
- ViewRootImpl 通过 Binder 与 WMS(WindowManagerService) 通信,申请窗口、布局、Surface 等。
WMS(WindowManagerService):系统级窗口管理
在 系统进程 中运行,统一管理所有 app 的窗口:
- 决定窗口的位置、尺寸、Z 轴顺序;
- 为窗口分配/更新对应的 Surface;
- 将窗口信息提供给 SurfaceFlinger 刷新显示。
关系图(结构视角):
scss
[ Activity ]
└─ [ Window (PhoneWindow) ]
└─ [ DecorView (ViewGroup 根) ]
↑
│ setContentView()
│
[ WindowManagerImpl ] (App进程)
└─ [ WindowManagerGlobal ]
└─ new ViewRootImpl(...)
└─ setView(DecorView, params)
↕ Binder
[ WMS (系统进程) ]
└─ 管理 Window、分配 Surface
四、Surface:ViewRootImpl 与图形系统的"接缝处"
Surface:底层缓冲队列的 Java 封装
-
每个 Window 在 WMS/SurfaceFlinger 侧都会有一个 Surface 对应;
-
对 ViewRootImpl 来说,它持有一个
Surface mSurface:- 这是它绘制输出的目标;
- 内部与 BufferQueue 关联,生产者是当前窗口(App 侧),消费者是 SurfaceFlinger。
ViewRootImpl.setView() & requestLayout()
ViewRootImpl.setView() 完成:
- 保存根 View = DecorView;
- 向 WMS 注册窗口;
- 初始化
Surface; - 调用
requestLayout(),触发第一次测量/布局/绘制。
requestLayout() 内部最终会走到:
scss
ViewRootImpl.requestLayout()
└─ scheduleTraversals()
└─ mChoreographer.postCallback(TRAVERSAL, mTraversalRunnable, ...)
这里就引出了 Choreographer。
五、Choreographer + Handler:VSync 驱动的 UI 刷新
Handler / Looper:消息循环基础
-
UI 线程拥有 Looper + MessageQueue;
-
Handler 用来:
- 发送消息、投递 Runnable;
- 在 UI 线程执行各种回调(包括 ViewRootImpl 的刷新逻辑)。
Choreographer:基于 VSync 的帧调度器
-
Choreographer 挂在 UI 线程上;
-
scheduleTraversals()会通过 Choreographer 在 下一个 VSync 时 执行doTraversal(),而不是立刻执行:- 这样能保证所有
requestLayout()/invalidate()合并到下一帧统一处理; - 避免"抖动"和无意义重复绘制。
- 这样能保证所有
简化版时序:
scss
requestLayout() / invalidate()
↓
ViewRootImpl.scheduleTraversals()
↓
Choreographer.postCallback(TRAVERSAL, doTraversal)
↓ (等到下一次 VSync)
Choreographer#doFrame()
└─ doTraversal() // 核心渲染流程
六、ViewRootImpl.doTraversal():三大流程 + Canvas / Skia / OpenGL ES
doTraversal() 里会顺序执行:
scss
performMeasure() → performLayout() → performDraw()
performMeasure()
-
从 DecorView 开始向下:
- 调用每个 View / ViewGroup 的
onMeasure(); - 计算 width/height。
- 调用每个 View / ViewGroup 的
performLayout()
-
对根 View 调用
layout(l, t, r, b):- ViewGroup 会在
onLayout()中安排子 View 的位置; - 整个树的几何结构确定。
- ViewGroup 会在
performDraw():真正的绘制
核心逻辑类似:
typescript
public void performDraw() {
// 省略前置逻辑
draw(fullRedrawNeeded);
}
private void draw(boolean fullRedrawNeeded) {
// 获取/准备 Canvas
// 调用 DecorView.draw(canvas)
}
调用链:
scss
ViewRootImpl.performDraw()
└─ DecorView.draw(Canvas)
└─ drawBackground()
└─ onDraw(Canvas)
└─ dispatchDraw(Canvas) // 调子 View 的 draw()
└─ 每个子 View 的 onDraw(Canvas)
Canvas / Skia / OpenGL ES
在 onDraw(Canvas) 中,开发者用 Canvas 提供的 API 绘制:
css
protected void onDraw(Canvas canvas) {
canvas.drawRect(...);
canvas.drawText(...);
canvas.drawBitmap(...);
}
底层发生了:
arduino
Canvas API
↓
Skia (2D 引擎)
↓
※ 若软件渲染:在 CPU 内存 buffer 中画像素
※ 若硬件加速(HWUI + OpenGL ES/Vulkan):
Skia 将绘制指令转换为 GPU 命令
↓
通过 OpenGL ES / Vulkan 调 GPU 渲染
↓
GPU 把结果写入当前 Surface 的 buffer