Android随笔-view是如何显示到屏幕上

面显示在屏幕上的完整链路,从 View 的 measure/layout/draw 到 SurfaceFlinger 合成 再到 LCD 像素发光 ,共经历 8 个阶段。

一、整体链路

二、Stage 1: View 体系 --- measure / layout / draw

这是应用开发最熟悉的部分,但发生在应用进程中

2.1 View 树结构

2.2 三大遍历

方法 作用 关键类
measure() 确定自身及子 View 的宽高 MeasureSpec (EXACTLY / AT_MOST / UNSPECIFIED)
layout() 确定自身及子 View 的位置 (left/top/right/bottom) ViewGroup.onLayout()
draw() 在 Canvas 上绘制内容 Canvas.drawText() / drawBitmap() / drawRect()

2.3 源码入口

java 复制代码
// ViewRootImpl.java --- 核心入口
private void performTraversals() {
    // 1. MEASURE
    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    // -> DecorView.onMeasure() -> 递归测量所有子 View
    
    // 2. LAYOUT
    performLayout(lp, desiredWindowWidth, desiredWindowHeight);
    // -> DecorView.onLayout() -> 递归布局所有子 View
    
    // 3. DRAW
    performDraw();
    // -> 从 Surface 获取 Canvas -> DecorView.draw() -> 递归绘制
}

三、Stage 2: ViewRootImpl --- 连接 View 与 SurfaceFlinger 的桥梁

3.1 ViewRootImpl 的核心作用

功能 说明
setView() 建立与 WMS 的连接,创建 Surface,启动 Choreographer
Choreographer 同步 VSync 信号(60Hz 每 16.67ms 触发一次),驱动 doFrame()
Surface 图形缓冲区句柄,应用在上面绘制,SF 合成它
SurfaceControl WMS 通过 SurfaceFlinger 创建,应用获得引用用于绘制

3.2 绘制提交

java 复制代码
// ViewRootImpl.performDraw()
private void performDraw() {
    // 1. 从 Surface 获取 Canvas
    Canvas canvas = mSurface.lockHardwareCanvas();
    // 硬件加速:lockHardwareCanvas() → OpenGL ES 后端
    // 软件渲染:lockCanvas() → Bitmap 后端
    
    // 2. 绘制整个 View 树
    mView.draw(canvas);  // DecorView.draw()
    // -> ViewGroup.dispatchDraw() → 每个子 View 的 draw()
    // -> TextView.onDraw(): canvas.drawText()
    // -> ImageView.onDraw(): canvas.drawBitmap()
    
    // 3. 提交缓冲区给 SurfaceFlinger
    mSurface.unlockCanvasAndPost(canvas);
    // -> queueBuffer() 到 BufferQueue
    // -> SurfaceFlinger 收到新缓冲区就绪通知
}

四、Stage 3: BufferQueue --- 生产者-消费者模型

每个 Surface 对应一个 BufferQueue,应用是生产者SurfaceFlinger 是消费者

Triple Buffering(三缓冲):

  • Front Buffer:正在显示
  • Back Buffer:SF 正在合成
  • Free Buffer:App 正在绘制

三缓冲防止 App 绘制和 SF 合成互相阻塞。

五、Stage 4: SurfaceFlinger --- 图层合成

5.1 SurfaceFlinger 的职责

  • 收集所有应用的 Surface(Layer)
  • 按 Z-Order 排序
  • 合成(Composite)为单一帧缓冲区
  • 通过 HWComposer 提交到显示硬件

5.2 合成流程

java 复制代码
// SurfaceFlinger::onMessageReceived()
void SurfaceFlinger::onMessageReceived(int32_t what) {
    // 1. VSync 到达
    // 2. 收集所有有新缓冲区的 Layer
    for (const auto& layer : mLayers) {
        if (layer->hasNewBuffer()) {
            layer->updateTexImage();  // acquire 新缓冲区
        }
    }
    
    // 3. 请求 HWC 合成
    hwcDevice->setDisplayContents(display, layers);
    
    // 4. 如果 HWC 能处理所有 Layer → 硬件 overlay 合成
    //    如果不能 → GPU 通过 OpenGL 合成剩余 Layer
    
    // 5. 提交到显示
    hwcDevice->presentDisplay(display);
}

5.3 合成方式

方式 说明 性能
HWC Overlay 硬件直接把 Layer 送到显示控制器,绕过 GPU 最优,零 GPU 开销
GPU Composition GPU 把所有 Layer 合成到一个 framebuffer 耗电,但兼容性好
Mixed HWC 处理部分 Layer,GPU 处理剩余 平衡

六、Stage 5: VSync 与帧率控制

6.1 VSync 机制

概念 说明
VSync 显示器垂直同步信号,60Hz 屏幕每 16.67ms 发一次
Choreographer App 注册 doFrame() 回调,VSync 到达时触发
Jank(掉帧) App 绘制超过 16ms,错过 VSync → 重复显示上一帧

6.2 时序示例

bash 复制代码
VSync 0:  App 绘制 Frame 0  ──> SF 合成 Frame 0  ──> 显示 Frame 0
VSync 1:  App 绘制 Frame 1  ──> SF 合成 Frame 1  ──> 显示 Frame 1
VSync 2:  App 绘制 Frame 2 (慢) 
VSync 3:  App 还在画 Frame 2  ──> SF 合成 Frame 1  ──> 显示 Frame 1 (JANK!)
VSync 4:  App 完成 Frame 2  ──> SF 合成 Frame 2  ──> 显示 Frame 2

七、Stage 6: 显示硬件 --- 从 Framebuffer 到像素发光

7.1 硬件链路

组件 作用
Framebuffer 最终合成图像在内存中的存储
Display Controller 硬件读取 framebuffer,通过 DSI/MIPI 发送给 LCD 面板
LCD Panel 物理像素:TFT 控制液晶偏转 + RGB 滤光片 + LED 背光
OLED Panel 每个像素自发光(R/G/B 子像素),无背光

7.2 LCD vs OLED

特性 LCD OLED
发光原理 背光穿透液晶 + 滤色片 有机材料自发光
对比度 有背光漏光,对比度低 纯黑像素不发光,对比度无限
响应速度 液晶偏转慢,有拖影 纳秒级响应
功耗 背光常亮,功耗固定 暗色场景功耗低
寿命 有机材料老化,可能烧屏

八、特殊渲染路径

类型 特点 适用场景
普通 View 走完整 measure/layout/draw,硬件加速 大多数 UI
TextureView 渲染到 OpenGL Texture,支持变换 视频播放、Camera 预览
SurfaceView 独立 Surface,绕过 View 层级,直接到 SF 游戏、视频(性能最优)
WebView 独立 Surface + GPU 进程 网页内容
Compose 直接操作 Canvas,无 View 层级 Jetpack Compose UI

九、完整链路总结

阶段 所在进程 核心操作 输出
1. measure App View.measure() 递归计算宽高 每个 View 的 measuredWidth/Height
2. layout App View.layout() 递归确定位置 每个 View 的 left/top/right/bottom
3. draw App View.draw() 递归绘制到 Canvas 图形缓冲区(Buffer)
4. submit App unlockCanvasAndPost()queueBuffer() Buffer 进入 BufferQueue
5. acquire SurfaceFlinger acquireBuffer() 获取新缓冲区 Layer 更新
6. composite SurfaceFlinger 合成所有 Layer 到 framebuffer 最终帧图像
7. present HWComposer presentDisplay() 提交到显示控制器 显示控制器读取
8. display Hardware 像素发光 人眼看到图像
相关推荐
xingxiliang1 天前
深入浅出 Android CTS 视频测试:从环境搭建、用例设计到底层通信原理
android·音视频
我命由我123451 天前
Android 开发问题:为 PDFView 设置一个带有黑色边框的背景 drawable,但边框没有生效
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
雨白1 天前
深入理解 Kotlin 协程 (八):拾遗补阙,探秘官方框架的调度细节与取消闭环
android·kotlin
海天鹰1 天前
PHP上传文件
android·开发语言·php
2501_915918411 天前
详解iOS App上架至App Store的全流程步骤与注意事项
android·macos·ios·小程序·uni-app·cocoa·iphone
码农coding1 天前
android12 SystemUI之StatusBar(二)
android
Lesile1 天前
Interview#1 历史演进:MVC · MVP · MVVM · MVI架构详解
android·android jetpack
杉氧1 天前
Flutter 像素级还原实战:用 CustomPaint 与 Bezier 曲线手绘精致图针
android·前端·flutter
我命由我123452 天前
Android 在构建过程中,发现有两个依赖库都包含了相同路径的资源文件
android·java·开发语言·java-ee·kotlin·android-studio·android runtime
Coffeeee2 天前
谷歌的一个优化建议,让我重新学了一遍Android里面如何正确处理位图
android·google·kotlin