在以前写传统 View 的时候,View 的绘制流程:
onMeasure->onLayout->onDraw。在 Compose 中,这个流程被重新抽象成了极其清晰的三个独立阶段:
标准回答模板: “在 Compose 中,UI 的完整执行流程被严格划分为三个阶段:
- 组合(Composition):执行 Composable 函数,构建 LayoutNode 树,并建立 State 的依赖追踪。解决‘要显什么’的问题。
- 布局(Layout):遍历 LayoutNode 树,通过单次测量(Single-pass Measure)计算各个节点的尺寸,并确定他们在屏幕上的坐标(Place)。解决‘放在哪里’的问题。
- 绘制(Drawing):将最终的像素指令绘制到 Canvas 上。解决‘长什么样’的问题。
Table of contents
Open Table of contents
一、Compose 的三大渲染阶段
第一阶段:组合 (Composition)
—— “要显示什么 UI?”
在这个阶段,Compose 框架会执行 @Composable 函数。
- 构建 UI 树: 当函数被调用时,它并不会直接
new出什么 View 对象,而是会在底层生成或更新一棵隐式的结构树(内部叫 Slot Table 槽位表,以及对应的 LayoutNode 树)。 - 记录状态依赖: 如果代码里读取了
State,Compose 会在这个阶段偷偷记下:“这个节点依赖了这个 State”。 - 结果: 这一步跑完,Compose 知道了 UI 的层级结构,以及它们分别叫什么名字、包含了什么内容。
第二阶段:布局 (Layout)
—— “放在屏幕的哪里?”
组合完成后,Compose 拿到了一棵 LayoutNode 树。接下来要决定它们的大小和位置。这个阶段又细分为两步:
- 测量 (Measure): 父节点向子节点传递尺寸约束(Constraints,比如最大宽度多少)。子节点根据约束测量自己的大小。(注意:这里就是“强制单次测量”原则生效的地方,绝对不允许父节点反复测子节点)。
- 放置 (Place): 子节点测好大小后,把自己的尺寸汇报给父节点。父节点根据这些信息,把子节点放置到具体的 X/Y 坐标上。
第三阶段:绘制 (Drawing)
—— “长什么样子?”
有了大小和位置,最后一步就是把这棵树画到屏幕(Canvas)上。
- Compose 会遍历 LayoutNode 树,调用各个节点的绘制指令(比如画背景色
Modifier.background、画文字轮廓等)。
二、状态读取与“阶段跳过”机制 (Phase Skipping)
如果面试时只答出上面三个阶段,能拿个 80 分。
但如果把“状态改变”和这三个阶段结合起来,告诉面试官如何跳过不必要的阶段,那就是妥妥的 100 分。
在传统 View 里,如果改了一个 View 的文字,通常会导致整个 View 树进行一次 requestLayout 和 invalidate,非常笨重。
而在 Compose 中,状态在哪里被读取,决定了重新渲染从哪个阶段开始 这就是 Compose 性能优化的极致体现。
场景 1:状态导致内容改变 -> 触发 Composition(走完整流程)
var text by remember { mutableStateOf("Hello") }
Text(text = text) // 读取了 State,改变了文本内容
流程: 当 text 改变,触发重组(Composition 阶段重新执行)-> 重新 Layout(因为字数变了,宽高可能变)-> 重新 Draw。
场景 2:状态只影响位置/大小 -> 跳过 Composition,直接 Layout
var offset by remember { mutableStateOf(0.dp) }
Box(modifier = Modifier.offset(offset)) { ... }
// 注意:如果只是修改偏移量,UI 树的结构没变,内容没变
流程: 当 offset 改变,Compose 极其聪明地发现:没改树的结构,也没改内容,只是改了位置。于是它完全跳过第一阶段(Composition),直接从第二阶段 Layout 开始,重新计算坐标并绘制。性能大幅提升!
场景 3:状态只影响颜色 -> 跳过 Composition 和 Layout,直接 Draw
var color by remember { mutableStateOf(Color.Red) }
Box(modifier = Modifier.drawBehind { drawRect(color) }) { ... }
// 颜色改变不影响大小和位置,也不影响结构
流程: 当 color 改变,Compose 跳过了第一阶段和第二阶段,直接通知底层 Canvas:“你只需要把颜色换一下重新画(Draw)即可”。这是性能开销最小的。
三、总结
Compose 最强大的架构设计在于它的**按需阶段跳过(Phase Skipping)**机制。
当我们修改一个 State 时,Compose 会精准判断这个 State 是在哪个阶段被读取的。
- 如果只是颜色改变,它会跳过组合和布局,直接触发绘制;
- 如果只是坐标改变,它会跳过组合,直接触发布局。
这种极细粒度的刷新机制,从底层架构上超越了传统 View 系统动辄全局 invalidate/requestLayout 的性能瓶颈。