一句话总结:
传统绘制优化的核心是“让每一次 onDraw 更快”,而现代优化的哲学则是“通过缓存和状态驱动,让 onDraw 根本不必被调用”。
第一篇章:“古典哲学”——让每一次绘制都物尽其用 (View 体系)
在传统的 View 体系中,我们的核心目标是降低 CPU 和 GPU 在每一帧的绘制开销。
1. 绘制的根源:invalidate() 与 onDraw()
当调用 view.invalidate() 时,我们为这个 View 标记了一个“脏区”,并请求系统在下一个 Vsync 信号到来时,回调 onDraw() 方法来重绘它。我们的优化,就是围绕这个过程展开。
2. 核心技巧:削减绘制开销
总结以下技巧,它们是优化存量代码的基石:
- 减少过度绘制 (Overdraw): 通过开发者选项的工具,移除所有不必要的
background,并使用canvas.clipRect()精确限定绘制区域,确保每个像素点只被绘制一次。 - 简化
onDraw()实现: 严禁在onDraw()中分配对象(new Paint(),new Rect()),所有绘图资源都应在初始化时创建并复用。 - 拥抱硬件加速: 开启硬件加速,将绘制任务从 CPU 转移到更高效的 GPU。
3. 深入本质:硬件加速的“缓存”魔法——DisplayList
开启硬件加速后,onDraw() 的角色发生了根本性转变:
- CPU 不再是“画家”,而是“导演”: 它将
canvas.drawXX()等命令录制成一个名为DisplayList的“剧本”。 - GPU 成为“演员”:
RenderThread将这个“剧本”交给 GPU,由 GPU 高效地执行并渲染。
优化的关键在于复用这个“剧本” 。一旦 DisplayList 被创建,只要 View 的内容本身不变,即便是对其进行平移、缩放、旋转、修改透明度等操作,系统都无需重新调用 onDraw() ,而是直接在 GPU 层面复用已缓存的 DisplayList 并应用新的变换。
启示:
- 优先使用属性动画(
view.animate()...),因为它能最大化地利用DisplayList缓存。 - 避免不必要的
invalidate(),因为每一次调用都可能导致DisplayList的重新录制。
第二篇章:“现代哲学”——从源头上避免绘制 (Jetpack Compose)
Jetpack Compose 带来了范式革命。它不再关心“如何画”,而是关心“画什么”。它通过状态驱动和智能重组,将绘制优化的思维提升到了新的高度。
1. 核心理念:UI 是状态的函数 UI = f(State)
在 Compose 中,你不再手动调用 invalidate()。你只负责描述 UI 应该如何根据某个状态(State)来呈现。当且仅当这个状态发生变化时,Compose 会自动、精准地找到依赖这个状态的 UI 部分,并仅仅重绘这一部分。
2. 智能重组 vs. invalidate()
invalidate()(View 体系): 像一个“全城警报”,通知范围大(整个 View),需要开发者手动优化onDraw内部的逻辑来减少实际工作量。- 智能重组 (Compose): 像一把“手术刀”,精准定位到数据变化的最小 UI 单元。如果一个
Text的文本状态变了,那么只有这个Text对应的 Composable 函数会重新执行。如果状态不变,相关的绘制代码根本不会被执行。 这就是“无需作画”的真谛。
@Composable
fun MyScreen(viewModel: MyViewModel) {
// a. nameState 是一个 State<String>
val name by viewModel.nameState.collectAsState()
// b. counterState 是一个 State<Int>
val counter by viewModel.counterState.collectAsState()
Column {
// 这个 Text 只订阅了 nameState
Text(text = name)
// 这个 Text 只订阅了 counterState
Text(text = "Counter: $counter")
Button(onClick = { viewModel.incrementCounter() }) {
Text("Increment")
}
}
}
当点击按钮,只有 counterState 变化时,Compose 只会重组(重绘)第二个 Text,第一个 Text 和 Button 的代码完全不会再次执行。
三、总结:你的优化工具箱
| 时代 | 核心哲学 | 关键工具/概念 | 适用场景 |
|---|---|---|---|
View 体系 | 让每一次绘制更高效 | DisplayList 缓存, 避免 Overdraw, 优化 onDraw | 维护和优化现有的基于 XML 的项目 |
| Compose 体系 | 通过状态管理避免不必要的绘制 | 智能重组 (Recomposition), State, Modifier | 开发新功能和新应用,追求极致性能和开发效率 |
最终建议:
对于存量项目,请用“古典哲学”精细打磨你的 View 绘制性能。对于新项目和新功能,请拥抱“现代哲学”,用 Jetpack Compose 从架构层面构建一个无需过度优化的、天然高性能的 UI。