一、状态管理:从入门到通透
状态管理,说白了就是解决「数据变了,UI怎么跟着变」的问题。我刚开始用 Jetpack Compose 时,最不适应的就是这点——以前写 XML 布局,改数据得手动调 findViewById 再 setText,现在呢?数据一变,UI 自动刷新。听起来很美好,但坑也不少。
这一章,我会把 Compose 状态管理的核心概念掰开揉碎讲清楚。你跟着走一遍,基本就能应付 90% 的开发场景了。
1. remember 与 mutableStateOf:最基础的状态容器
先看一个最简单的例子:
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("点击了 $count 次")
}
}
这段代码里,mutableStateOf(0) 创建了一个可观察的状态对象,初始值是 0。remember 的作用是让这个对象在重组时不被重新创建。嗯,这里要注意:没有 remember,每次重组都会重新生成一个状态,那之前的数据就全丢了。
核心理解:
mutableStateOf创建的是MutableState<T>对象by关键字是 Kotlin 的委托属性语法,等价于count.valueremember保证状态在重组时存活
我个人习惯用 by 委托,代码更简洁。但如果你团队里有新人,写成 count.value 可能更直观——看个人喜好。
2. State 与 MutableState:读写分离的设计
Compose 把状态分成了两个接口:
| 接口 | 能力 | 使用场景 |
|---|---|---|
State<T> |
只读,只能获取 .value |
对外暴露给其他组件 |
MutableState<T> |
可读可写,能修改 .value |
内部管理数据 |
为什么要分开?我在项目中遇到过一个问题:一个子组件不小心改了父组件的状态,导致整个界面乱跳。后来我强制要求:对外只传 State,内部用 MutableState。这样谁改谁负责,责任清晰。
小技巧:用 asState() 可以把 MutableState 转成 State 暴露出去,但 Compose 里更常见的做法是直接用 by 委托,然后只暴露 getter。
3. 状态提升:把数据往上提
状态提升是 Compose 里最重要的设计模式之一。说白了就是:谁用数据,谁管理数据;多个组件共享数据,就把数据提到它们的共同父级。
举个例子:
@Composable
fun Parent() {
var text by remember { mutableStateOf("") }
ChildA(text = text)
ChildB(onTextChange = { text = it })
}
@Composable
fun ChildA(text: String) {
Text("当前输入:$text")
}
@Composable
fun ChildB(onTextChange: (String) -> Unit) {
TextField(value = "", onValueChange = onTextChange)
}
你看,text 状态在 Parent 里管理,ChildA 只读,ChildB 通过回调修改。这就是典型的状态提升——数据往父级提,子组件只负责展示和通知。
我曾经踩过的坑:把状态放在太底层的组件里,结果上层想用的时候拿不到,只能一层层传回调。最后代码又臭又长。记住一个原则:如果一个状态被两个以上不相关的组件使用,就往上提一层。
4. ViewModel 集成:真正的工程化方案
remember 虽然方便,但有个致命问题:屏幕旋转或进程重启时,数据就丢了。这时候就需要 ViewModel 出场。
class MyViewModel : ViewModel() {
private val _count = mutableStateOf(0)
val count: State<Int> = _count
fun increment() {
_count.value++
}
}
@Composable
fun MyScreen(viewModel: MyViewModel = viewModel()) {
val count by viewModel.count
Text("计数:$count")
}
ViewModel 的生命周期比 Composable 长,它会在 Activity 重建时存活。我一般把业务逻辑都放在 ViewModel 里,Composable 只负责 UI 渲染——这样测试也好写,逻辑也好复用。
工程实践建议:
- UI 状态用
mutableStateOf放在 ViewModel 里 - 复杂业务逻辑用
viewModelScope.launch处理异步 - Composable 里只调用
viewModel的方法,不直接操作状态
5. LiveData / Flow 转 State:桥接旧世界
很多项目之前用的是 LiveData 或 Flow,不可能全改成 mutableStateOf。Compose 提供了现成的转换函数:
// LiveData 转 State
val name by viewModel.userName.observeAsState("")
// Flow 转 State
val data by viewModel.dataFlow.collectAsState(initial = null)
我个人更推荐 Flow,因为它在 ViewModel 里更好用——stateIn 操作符可以配合 SharingStarted 控制共享策略。不过如果你团队已经习惯了 LiveData,也没必要强行换。
注意:collectAsState 会在 Composable 进入组合时开始收集,离开时自动取消。不用担心内存泄漏。
知识体系总览
下面这张图总结了本章的核心内容:
本章小结
状态管理是 Compose 的基石。你只要记住几个关键点:
- remember 保活,mutableStateOf 创建可观察数据
- State 只读,MutableState 可写——对外暴露时注意封装
- 状态提升 解决组件间数据共享,数据往父级提
- ViewModel 解决生命周期问题,适合工程化
- LiveData/Flow 可以通过扩展函数无缝接入 Compose
这些概念你多写几个页面自然就熟了。下一章我们会深入 Compose 的布局系统,看看那些好用的 Column、Row、Box 到底怎么玩出花来。
公众号:蓝海资料掘金营,微信deep3321