14、Compose KTX:Compose与KTX的结合,remember与mutableStateOf的扩展
Compose 出来之后,很多人第一反应是:「这不就是 Android 版的 SwiftUI 吗?」
嗯,表面看确实像。但真正用起来你会发现,Compose 的底层逻辑和 View 体系完全不同。
而 KTX 在 Compose 里扮演的角色,说白了就是帮我们把那些「本该由框架做、但框架没做」的琐事给补上。
我个人习惯把 Compose KTX 理解为「胶水层」。它不改变 Compose 的核心理念,而是让 Kotlin 的语法糖和 Compose 的状态管理结合得更自然。
今天我们就来聊聊 remember 和 mutableStateOf 这两个最常用的 API,看看 KTX 是怎么给它们「加 buff」的。
14.1 remember:不只是缓存
remember 是 Compose 里最基础的记忆机制。
它的作用是在重组时保留值,避免重复计算。
但如果你只把它当缓存用,那就太小看它了。
我在项目中遇到过一个问题:某个列表项需要根据网络状态显示不同图标。
一开始我直接在 Composable 里写 if (isOnline) ... else ...,结果每次重组都重新创建图标对象。
后来改成 remember(isOnline) { if (isOnline) IconA else IconB },性能明显提升。
核心要点:
remember的 key 参数决定了何时重新计算- 不传 key 时,只在首次进入组合时计算
- 传了 key,key 变化时才会重新计算
你想想看,如果 key 传的是一个频繁变化的值,那 remember 就退化成普通变量了。
所以选 key 要谨慎,别把整个 ViewModel 传进去。
14.2 mutableStateOf:可观察的容器
mutableStateOf 是 Compose 状态管理的基石。
它包装的值一旦改变,就会触发读取它的 Composable 重组。
但有个细节很多人忽略:mutableStateOf 默认是线程安全的吗?
答案是否定的。它只在 Compose 的调度器下保证一致性。
如果你在后台线程修改它,可能会引发奇怪的问题。
我曾经踩过的坑:
在协程的 Dispatchers.IO 里直接修改 mutableStateOf 的值,结果 UI 没有更新。
后来发现需要切回 Dispatchers.Main 或者用 snapshotFlow 做桥接。
所以我的建议是:
所有状态修改都放在 Compose 的上下文中,或者通过 LaunchedEffect 来驱动。
14.3 KTX 带来的扩展
Compose KTX 提供了一些非常实用的扩展函数,让 remember 和 mutableStateOf 用起来更顺手。
| 扩展函数 | 作用 | 示例 |
|---|---|---|
rememberSaveable |
在配置变更时保存状态 | rememberSaveable { mutableStateOf(0) } |
derivedStateOf |
根据其他状态派生新状态 | val isEven by remember { derivedStateOf { count % 2 == 0 } } |
mutableStateListOf |
可观察的列表 | val list = remember { mutableStateListOf |
我个人最常用的是 rememberSaveable。
以前用 ViewModel 保存状态,后来发现很多简单场景根本不需要 ViewModel。
一个 rememberSaveable 就能搞定屏幕旋转后的状态恢复。
小技巧:
rememberSaveable 默认使用 Bundle 存储,所以只能保存基本类型和 Parcelable。
如果需要保存复杂对象,可以自定义 Saver。
14.4 实战:一个计数器组件
光说不练假把式。我们写一个带历史记录的计数器,看看这些 API 怎么配合。
@Composable
fun CounterWithHistory() {
val count = rememberSaveable { mutableStateOf(0) }
val history = remember { mutableStateListOf<Int>() }
val isEven by remember {
derivedStateOf { count.value % 2 == 0 }
}
Column(modifier = Modifier.padding(16.dp)) {
Text("当前值: ${count.value}")
Text("是否为偶数: $isEven")
Button(onClick = {
count.value++
history.add(count.value)
}) {
Text("加一")
}
Button(onClick = {
if (history.isNotEmpty()) {
history.removeLast()
count.value = history.lastOrNull() ?: 0
}
}) {
Text("撤销")
}
Text("历史记录:")
history.forEach { value ->
Text("- $value")
}
}
}
这段代码里:
rememberSaveable保证旋转屏幕后 count 不丢失mutableStateListOf让列表变化也能触发重组derivedStateOf自动计算 isEven,避免手动同步
嗯,这里要注意:mutableStateListOf 的修改操作(add/remove)会自动通知 Compose。
但如果你直接替换整个列表,比如 history = newList,那就不会触发重组了。
这是 Compose 的「引用不变」原则,和 React 的不可变数据有点像。
14.5 知识体系图
下面这张图展示了 Compose KTX 中状态管理的核心关系:
从图中可以看出,Compose 的状态管理以 remember 和 mutableStateOf 为根基,KTX 扩展在此基础上提供了更便捷的 API。
三者配合,基本能覆盖 90% 的 UI 状态场景。
14.6 避坑指南
最后分享几个我踩过的坑:
- 不要在 remember 里做耗时操作:remember 是在组合阶段执行的,如果里面做了网络请求或数据库查询,会卡 UI 线程。
- mutableStateOf 不要和 LiveData 混用:两者虽然都是可观察的,但生命周期不同。混用容易导致内存泄漏或状态不同步。
- rememberSaveable 不是万能的:它只能保存简单数据。如果保存大对象,建议用 ViewModel + SavedStateHandle。
总结一下:
Compose KTX 让状态管理变得更 Kotlin 化。
remember 负责记忆,mutableStateOf 负责通知,KTX 扩展负责让两者更好用。
掌握这三者的关系,你就能写出既高效又优雅的 Compose 代码。
公众号:蓝海资料掘金营,微信deep3321