浅谈View经GPU渲染绘制的过程

咱们今天就用一个超有趣的故事来理解Android的View绘制原理。准备好了吗?

故事开始:UI王国的"画展"筹备记

想象一下,Android系统就像一个庞大的UI王国,每次打开App就像举办一场画展。

角色介绍:

  • ViewRootImpl:画展总策划(大管家)
  • DecorView:画展的巨型画布
  • View/ViewGroup:各种画家和画师小组
  • Choreographer:动画导演(负责节奏)
  • SurfaceFlinger:最终展厅布置师
  • CPU/GPU:画家们的双手

第一幕:画展的启动(Activity创建)

java 复制代码
// 王国收到举办画展的指令
public class Activity {
    public void onCreate() {
        setContentView(R.layout.main); // "准备画布和颜料!"
    }
}

当Activity创建时,PhoneWindow会准备DecorView这个"巨型画布",然后通过LayoutInflater把XML布局变成一个个View对象。

第二幕:三大核心流程 - 画展筹备三部曲

1. 测量(Measure)- "量尺寸"

总策划ViewRootImpl大喊:"各位画家,报上你们的尺寸!"

java 复制代码
// ViewRootImpl.java
private void performTraversals() {
    // 第一步:测量
    performMeasure();
    // 第二步:布局
    performLayout();
    // 第三步:绘制
    performDraw();
}

// 测量过程 - 递归测量所有View
private void performMeasure() {
    int childWidthMeasureSpec = ...;
    int childHeightMeasureSpec = ...;
    mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

每个View的measure方法:

java 复制代码
// View.java
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    onMeasure(widthMeasureSpec, heightMeasureSpec); // 自定义测量逻辑
    // 测量完成后,mMeasuredWidth/mMeasuredHeight就有值了
}

有趣比喻:就像画家们互相商量:"我要占多大地方?你有多宽?我有多高?"

2. 布局(Layout)- "定位置"

"现在开始布置展位!每个人站在哪里?"

java 复制代码
// ViewGroup.java
@Override
public final void layout(int l, int t, int r, int b) {
    // 先确定自己的位置
    super.layout(l, t, r, b);
    // 再安排子View的位置
    layoutChildren();
}

protected void layoutChildren() {
    for (int i = 0; i < getChildCount(); i++) {
        View child = getChildAt(i);
        // 计算每个子View的位置
        child.layout(childLeft, childTop, childRight, childBottom);
    }
}

3. 绘制(Draw)- "动笔作画"

"各就各位,开始画画!"

java 复制代码
// View.java
public void draw(Canvas canvas) {
    // 步骤1:画背景
    drawBackground(canvas);
    // 步骤2:画自己(核心)
    onDraw(canvas);
    // 步骤3:画子View
    dispatchDraw(canvas);
    // 步骤4:画装饰(滚动条、前景等)
    onDrawForeground(canvas);
}

第三幕:同步与渲染 - 动画导演登场

Choreographer(动画导演)负责保持60fps的流畅节奏:

java 复制代码
// Choreographer.java
public void onVsync() {
    // 垂直同步信号到来!
    mHandler.post(mTraversalRunnable);
}

final class TraversalRunnable implements Runnable {
    public void run() {
        doTraversal(); // 执行遍历(测量-布局-绘制)
    }
}

第四幕:硬件加速 - GPU大神出手

现代Android使用硬件加速,就像请来了GPU这位"神笔马良":

java 复制代码
// ThreadedRenderer.java(硬件渲染器)
@Override
void draw(View view, AttachInfo attachInfo) {
    // 1. 创建DisplayList(绘制指令列表)
    updateRootDisplayList(view);
    // 2. 同步到RenderThread
    nSyncAndDrawFrame(mNativeProxy);
}

DisplayList就像给GPU的"绘画说明书",避免重复计算。

第五幕:最终展示 - SurfaceFlinger布展

cpp 复制代码
// SurfaceFlinger.cpp(Native层)
void SurfaceFlinger::composite() {
    // 收集所有Layer(图层)
    for (auto layer : mLayers) {
        layer->latchBuffer(); // 获取图形缓冲区
        layer->prepareClientComposition(); // 准备合成
    }
    // 最终合成并显示
    doComposition();
    postFramebuffer(); // 输出到屏幕
}

完整时序图:画展全流程

关键技巧:性能优化秘诀

  1. 减少层级:画展布置不要太复杂(View层级扁平化)
  2. 避免过度绘制:不要在同一区域反复画画
  3. 使用硬件加速:让GPU大神多干活
  4. View复用:像 recyclerView 一样,重复利用画布
java 复制代码
// 优化示例:自定义View避免无效重绘
@Override
protected void onDraw(Canvas canvas) {
    if (needRedraw) { // 只有需要时才重绘
        drawContent(canvas);
        needRedraw = false;
    }
}

总结:一场精密的协作

Android View的绘制就像一场精心组织的画展:

  • ViewRootImpl是总策划,协调整个流程
  • 测量-布局-绘制是核心三部曲
  • Choreographer保证60fps的流畅节奏
  • GPU硬件加速提供强大的绘制能力
  • SurfaceFlinger是最终的舞台总监

理解了这套机制,你就能写出更流畅的UI,成为真正的Android UI大师!

怎么样,同学,现在是不是对View绘制有了更生动的理解?

相关推荐
阿巴斯甜16 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker17 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952718 小时前
Andorid Google 登录接入文档
android
黄林晴19 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android