硬件加速的核心不是“打开 GPU 就一定更快”,而是把绘制流程拆成两部分:CPU 负责记录绘制命令,GPU 负责把命令转换成像素。
核心结论
- 硬件加速后,
onDraw()仍然会在主线程执行,但绘制命令会被记录成 DisplayList。 - GPU 负责光栅化,把图形命令转换成最终像素。
- 硬件层可以缓存 View 的绘制结果,适合短期属性动画。
- 硬件层不是越多越好,大面积或长期启用会增加显存压力。
未启用硬件加速
简化流程:
View 树遍历
-> measure/layout
-> CPU 执行 onDraw()
-> CPU 光栅化
-> 写入图形缓冲区
-> 显示
此时 CPU 既要处理业务逻辑、布局计算,也要承担像素计算,复杂页面容易成为瓶颈。
启用硬件加速
简化流程:
View 树遍历
-> measure/layout
-> CPU 执行 onDraw() 并记录 DisplayList
-> RenderThread / GPU 消费绘制命令
-> GPU 光栅化
-> 写入 Buffer
-> SurfaceFlinger 合成显示
这里的关键变化是:Canvas 的很多调用不再立即生成像素,而是记录成绘制指令。GPU 后续可以并行处理这些指令。
DisplayList 的意义
DisplayList 可以理解为 View 绘制结果的命令列表:
drawRect(...)
drawText(...)
drawBitmap(...)
clipPath(...)
如果 View 内容没有变化,系统可以复用已有 DisplayList,减少主线程重复记录绘制命令的成本。
但如果调用了 invalidate(),对应 View 的 DisplayList 仍然需要重新记录。
硬件层
硬件层会把 View 的绘制结果缓存成 GPU 纹理:
View -> DisplayList -> GPU 光栅化 -> 离屏纹理
后续如果只是做平移、缩放、旋转、透明度变化,就可以直接复用纹理合成,不必重新绘制 View 内容。
适合场景:
- 短时间属性动画;
- 小面积复杂 View;
- 内容不变,只做变换。
不适合场景:
- 大面积列表;
- 每帧内容都变化的自定义 View;
- 长期启用的普通控件;
- 内存紧张的页面。
使用方式
view.setLayerType(View.LAYER_TYPE_HARDWARE, null)
view.animate()
.translationY(100f)
.alpha(0f)
.withEndAction {
view.setLayerType(View.LAYER_TYPE_NONE, null)
}
回看清单
- 硬件加速不是跳过
onDraw(),而是改变绘制命令的执行方式。 - CPU 负责记录命令,GPU 负责光栅化和合成。
- DisplayList 可以减少重复记录绘制命令。
- 硬件层适合短期动画缓存,不适合长期全局开启。
- 判断是否需要硬件层,要看 View 内容是否稳定、面积是否可控、动画是否只做属性变换。