Skip to content

运行时原理 (Runtime Internals) ​

Compose 的魔法核心在于它如何存储和更新 UI 树。这与传统的 View 系统(对象树)完全不同。

1. 什么是 Gap Buffer? ​

Compose 的数据结构设计灵感来自于文本编辑器。

文本编辑器需要频繁插入和删除字符。如果使用数组,每次插入都需要移动后面所有元素,效率极低。 Gap Buffer 是一个包含“间隙(Gap)”的数组。当光标移动时,Gap 也随之移动。在 Gap 处插入元素是 O(1) 的。

Compose 使用类似的结构来存储 UI 树的信息,因为它假设 UI 的变化也是局部的。

2. 插槽表 (Slot Table) ​

Compose 并不维护一个由 View 对象组成的树。相反,它将所有信息展平存储在一个线性数组中,称为 Slot Table。

数组中存储了:

  • Composable 函数的参数
  • remember 的值
  • UI 节点的引用
  • 控制流信息 (if/else 标记)
text
[Group Start, Key: 123] 
  [Data: "Hello"]       // 参数
  [Group Start, Key: 456]
    [Node: TextNode]    // UI 节点
  [Group End]
[Group End]

3. 为什么 remember 能记住值? ​

当 remember { mutableStateOf(0) } 执行时:

  1. 初次组合 (Initial Composition):

    • Compose 在 Slot Table 当前位置写入计算结果。
    • 移动 Gap 指针。
  2. 重组 (Recomposition):

    • Compose 再次执行到这里。
    • 它查看 Slot Table 当前位置是否有值。
    • 如果有,直接读取(这就“记住”了)。
    • 如果参数变了(key 变了),它会更新 Slot Table 中的值。

4. 组 (Groups) ​

Slot Table 中的基本单位是 Group。Group 可以是:

  • Replaceable Group: 对应 if/else 等控制流。如果结构变了,整个 Group 被替换。
  • Movable Group: 对应 key()。如果 Group 只是换了位置,Compose 会移动数据而不是销毁重建。
  • Restartable Group: 对应 Composable 函数。它是重组的最小边界。

5. Applier ​

Compose Runtime 是通用的,它只负责管理 Slot Table 和变更差异 (Diff)。

Applier 负责将这些差异应用到具体的平台上。

  • UiApplier: 操作 Android View / LayoutNode。
  • VectorApplier: 操作矢量图节点。
  • Mosaic: 操作控制台字符(是的,Compose 可以写命令行 UI)。
  • Redwood: 操作原生 iOS/Web 节点(用于 CMP)。