第1章 State Hoisting 核心概念
State Hoisting(状态提升)是 Jetpack Compose 和 MVVM 架构的基石——将状态从可变的局部 UI 提升到最近的共同父级(即 ViewModel),使 UI 成为纯函数,状态可预测、可测试、可复用。本章从定义、职责边界、数据流原则三个维度建立核心认知,并给出架构边界、信任链路与落地验收框架。
1.1 状态提升的定义
状态提升指将组件的内部可变状态移到父级或外层持有者,父级以只读方式下发状态,子级通过回调上报用户意图。在 MVVM 中,ViewModel 就是那个“父级”。
| 维度 | 说明 |
|---|---|
| 状态共享 | 多个 UI 组件可读取同一份 ViewModel 状态,避免同步问题 |
| 单向数据流 | 状态只能向下传递,事件只能向上传递 |
| 可预测性 | 状态变化路径唯一,便于调试和回溯 |
架构边界提示:State Hoisting 的边界是 ViewModel 层,UI 层不得持有可变状态(局部动画等临时状态除外)。信任链路:View → ViewModel → Repository → DataSource,每层只信任下一层返回的数据。
1.2 与 ViewModel 职责边界
| 角色 | 职责 | 不可推卸责任 |
|---|---|---|
| View | 渲染状态、上报事件、动画协调 | 不得维护业务状态、不得直接修改 Model |
| ViewModel | 持有提升状态、处理业务逻辑、转换数据 | 不得持有 UI 引用、不得感知生命周期(除 clear 外) |
| Model | 数据持久化、网络请求、本地存储 | 不感知 ViewModel 和 View |
ViewModel 是状态提升的唯一合法容器。所有StateFlow、LiveData、mutableStateOf 必须声明在 ViewModel 作用域内。UI 层只能通过 .collectAsState() 或 .observe() 订阅只读状态。
常见风险点:将状态提升到 ViewModel 之外的全局单例(如静态 object),会导致测试困难、生命周期泄漏、线程安全问题。信任链路必须终止于 ViewModel,不可越级。
1.3 单向数据流原则
单向数据流(UDF)是 State Hoisting 的运行规则:
- 数据流(白色箭头):Model → ViewModel → View,状态不可变且向下流动
- 事件流(深色箭头):View → ViewModel → Model,用户动作以事件形式向上传播
- 控制流:ViewModel 是唯一决策者,决定何时触发数据刷新、如何处理副作用
以下代码片段展示了 Compose 中典型的 UDF 模式:
// ViewModel
class MyViewModel : ViewModel() {
private val _state = MutableStateFlow(MyUiState())
val state: StateFlow<MyUiState> = _state.asStateFlow()
fun onButtonClick() {
_state.update { it.copy(loading = true) }
// 触发 repository 等异步操作
}
}
// View (Composable)
@Composable
fun MyScreen(viewModel: MyViewModel = viewModel()) {
val uiState by viewModel.state.collectAsState()
Button(onClick = { viewModel.onButtonClick() }) {
Text(if (uiState.loading) "加载中…" else "点击")
}
}
状态 uiState 由 ViewModel 提升维护,UI 只读取并触发事件。整个信任链路清晰:UI 不信任自己维护的状态,只信任 ViewModel 下发的不可变快照。
架构选型权衡
| 方案 | 优势 | 劣势 |
|---|---|---|
| State Hoisting + ViewModel | 可测试、可复用、生命周期安全 | 增加 ViewModel 类数量,小型界面略显繁重 |
| 局部状态(Composable 内 remember) | 简单快速 | 无法跨组件共享、难以测试、生命周期耦合 |
| 全局单例状态 | 跨页面共享方便 | 内存泄漏、线程竞争、测试困难 |
验收标准(第1章)
✅ 项目中每个业务界面至少有一个 ViewModel 承担状态提升职责
✅ UI 代码中不出现
✅ 事件处理函数全部定义在 ViewModel 内,UI 仅调用方法
✅ 可通过替换 ViewModel 实现(测试替身)验证单向数据流正确性
✅ 项目中每个业务界面至少有一个 ViewModel 承担状态提升职责
✅ UI 代码中不出现
MutableStateFlow、mutableStateOf 等可变声明✅ 事件处理函数全部定义在 ViewModel 内,UI 仅调用方法
✅ 可通过替换 ViewModel 实现(测试替身)验证单向数据流正确性
图 1-1 State Hoisting 在 MVVM 中的分层与双向数据流示意。实线为数据流,虚线为事件流,黄色虚线框标识提升边界。