从Activity.setContentView()开始

一、从 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。

performLayout()

  • 对根 View 调用 layout(l, t, r, b)

    • ViewGroup 会在 onLayout() 中安排子 View 的位置;
    • 整个树的几何结构确定。

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
相关推荐
深海呐15 分钟前
Android WebView吊起软键盘遮挡输入框的问题解决
android·webview·android 键盘遮挡·webview键盘遮挡
摘星编程17 分钟前
RAG的下一站:检索增强生成如何重塑企业知识中枢?
android·人工智能
fatiaozhang952724 分钟前
基于slimBOXtv 9.19 V2(通刷S905L3A/L3AB)ATV-安卓9-通刷-线刷固件包
android·电视盒子·刷机固件·机顶盒刷机·slimboxtv9.19v2·slimboxtv
左绍骏1 小时前
01.学习预备
android·java·学习
鹏程十八少1 小时前
破解Android悬浮窗遮挡无障碍服务难题:我在可见即可说上踩过的坑
android·前端·面试
Kapaseker1 小时前
前端已死...了吗
android·前端·javascript
Winston Wood1 小时前
Android图形与显示系统经典故障解决方案:从源码到实操
android·图形系统·显示系统
Full Stack Developme2 小时前
Mycat 2 实现 MySQL 读写分离,并且实现 主从同步
android·数据库·mysql
Winston Wood2 小时前
Android图形与显示系统:从架构到协作的深度解析
android·图形系统·显示系统
lxysbly2 小时前
psx模拟器安卓版带金手指
android