前言
作为一名有着十年 Android 原生开发经验的开发者,我们已经习惯了 XML 布局、View.java 的漫长继承链以及 findViewById 的肌肉记忆。然而,随着 Jetpack Compose 的成熟及其在 Kotlin Multiplatform (KMP) 中的统治地位,UI 开发范式正在经历一场底层革命。
Compose 究竟是 XML 的一层"皮",还是彻底重造了轮子?它与 Android Framework 还有什么关系?本文将带你剥开 Compose 的外壳,直击其架构核心。
🏗️ 一、 整体架构:去 View 化的"渲染引擎"
在传统的 View 体系中,UI 是由一棵巨大的 View 对象树构成的。而在 Compose 中,UI 被彻底**"函数化"**了。
1. 彻底的"去 View 化"
Compose 并不是将 Text 映射为 TextView。它拥有一套独立的渲染闭环:
- 节点树 (Slot Table):Compose 维护的是一份名为"槽位表"的数据结构,而非厚重的 View 对象。
- 渲染引擎 (Skia) :在 Android 端,Compose 绕过了
android.widget,直接通过底层的 Skia 图形库在 Canvas 上绘制像素。这与游戏引擎(如 Flutter 或 Unity)的思路极其相似。
2. 载体:与 Framework 的"弱连接"
虽然渲染是独立的,但 Compose 依然需要一个载体进入系统。
- ComponentActivity :通过
setContent { ... }注入一个ComposeView。 - 角色分配 :Framework 现在仅负责提供 Window (画板) 、Lifecycle (生命周期) 和 Input (点击事件)。至于 UI 内部如何重绘、如何排版,Framework 完全不再过问。
🔄 二、 核心原理: UI=f(State)
Compose 的核心本质是一个状态机。
1. Recomposition (重组)
这是 Compose 最具魔力的部分。当状态(State)发生变化时,Compose 会自动重新执行受影响的 Composable 函数。
- 快照系统 (Snapshot System) :Compose 内部有一套类似 Git 的版本控制机制。当你在一个协程里修改了
MutableState,快照系统会标记受影响的函数。 - 局部刷新 :与
notifyDataSetChanged全刷不同,Compose 能精准地只更新那一行文字,而跳过旁边未变的按钮。
2. 布局协议:O(n) 的单次测量
传统 View 系统最怕深层嵌套(会导致指数级增长的测量开销)。
- 测量约束:Compose 规定父布局必须一次性完成子项的测量,严禁多次测量。
- 性能飞跃:这意味着在 Compose 中,你可以为了逻辑清晰而嵌套任意多层布局,而不会产生性能惩罚。
🌍 三、 为什么它是 KMP 的唯一选择?
正因为 Compose 与 Android Framework 脱钩了,它才具备了**"像素级跨端"**的能力。
- 在 iOS 上 :它申请一个
UIViewController,然后在上面盖一块 Skia 画布。你在 Android 上写的Box和Column,在 iOS 上运行的是完全相同的 C++ 绘图指令。 - 全栈统一:这种架构保证了无论是在 Android、iOS 还是 Web,你的 UI 逻辑、动效参数、甚至是一个圆角的弧度,都是绝对一致的。
🧠 四、 老兵的学习建议:忘掉 View,拥抱状态
如果你有多年原生经验,系统学习 Compose 时最重要的不是记 API,而是建立以下心智模型:
- 忘掉 Context,拥抱 CompositionLocal:不要再到处传递 Context,学会使用 Compose 环境下的局部变量。
- 理解副作用 (Side Effects) :弄清楚
LaunchedEffect与 Activity 生命周期的对应关系。 - 状态提升 (State Hoisting):这是 MVI 架构在 Compose 中的最佳实践,确保你的 UI 是纯净的、无状态的。
结语
Jetpack Compose 不是一个简单的 UI 库,它是 Android UI 开发史上最深刻的范式转移。它让我们从"手动维护 UI 状态"的苦差事中解脱出来,回归到"描述 UI 逻辑"的本质。
现在的 Compose 之于 Android,就像十年前的 Kotlin 之于 Java。 既然已经踏上了 KMP 的全栈之路,那么系统地掌握 Compose,就是你重塑技术护城河的最后一块拼图。
本文首发于掘金。如果你对 Compose 渲染底层或者 KMP 集成有更多疑问,欢迎在评论区交流!