从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
相关推荐
感谢地心引力17 小时前
安卓、苹果手机无线投屏到Windows
android·windows·ios·智能手机·安卓·苹果·投屏
优雅的潮叭21 小时前
cud编程之 reduce
android·redis·缓存
2601_9496130221 小时前
flutter_for_openharmony家庭药箱管理app实战+用药知识详情实现
android·javascript·flutter
一起养小猫21 小时前
Flutter for OpenHarmony 实战 表单处理与验证完整指南
android·开发语言·前端·javascript·flutter·harmonyos
2601_9499750821 小时前
flutter_for_openharmony城市井盖地图app实战+附近井盖实现
android·flutter
倾云鹤21 小时前
通用Digest认证
android·digest
我是阿亮啊1 天前
Android 自定义 View 完全指南
android·自定义·自定义view·viewgroup
2601_949833391 天前
flutter_for_openharmony口腔护理app实战+意见反馈实现
android·javascript·flutter
峥嵘life1 天前
Android 16 EDLA测试STS模块
android·大数据·linux·学习
TheNextByte11 天前
如何打印Android手机联系人?
android·智能手机