State 与 MutableState 基础:remember 与 mutableStateOf 创建可观察状态
原理:Compose 的声明式状态驱动重组
在 Jetpack Compose 中,UI 不是手动更新的,而是通过 状态 驱动的。当状态发生改变时,Compose 框架会自动重组(Recompose)该状态所关联的 Composable 函数,从而更新 UI。这一机制的核心是 可观察状态(Observable State)。
MutableState 是 Compose 中最基础的观察容器,它持有一个可变的值,并在值变化时通知 Compose 框架。
remember 函数用于保持对象跨重组(Recomposition)的存在性;没有 remember 包裹的状态会在每次重组时被重新创建,从而丢失变化。
实现流程:从创建到触发重组
- 在 Composable 函数内通过
mutableStateOf()创建初始状态(返回MutableState)。 - 用
remember将状态包裹,避免在每次重组时被重建。 - 读取
value属性(或使用 Kotlin 委托by)来驱动 UI。 - 修改
value时,Compose 检测到变化,自动触发包含该 state 读取的 Composable 重组。
💡 委托技巧: 使用
by 委托后的变量可以直接读写,例如 var count by remember { mutableStateOf(0) },代码更简洁。
核心代码示例:计数器
@Composable
fun CounterExample() {
// 使用 remember 保持状态,mutableStateOf 创建可观察状态
var count by remember { mutableStateOf(0) }
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "你点击了 $count 次",
style = MaterialTheme.typography.headlineMedium
)
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = { count++ }) {
Text("增加")
}
Spacer(modifier = Modifier.height(4.dp))
Button(onClick = { count = 0 }) {
Text("重置")
}
}
}
API 要点速查表
| API | 功能 | 注意 |
|---|---|---|
mutableStateOf(initial) |
创建一个 MutableState 实例,持有值 |
需要搭配 remember 使用 |
remember { … } |
在首次组成时计算,后续重组跳过计算并返回缓存值 | 不传入 keys 时永远返回首次缓存;传入 keys 时若 keys 变化则重新计算 |
by 委托 |
将 MutableState 转为常规 Kotlin 属性读写 |
需要 import import androidx.compose.runtime.getValue 和 setValue |
.value |
直接访问/修改 MutableState 的底层值 |
不使用 by 时必须显式读写 .value |
调试:验证状态与重组
- Android Studio Layout Inspector:部署应用后,使用 Layout Inspector 观察组件树,可以看到被重组的节点标记。
- Compose 编译器日志:在 build.gradle 中开启
composeCompilerMetrics可输出重组统计。 - 手动日志:在 Composable 函数内打印日志(例如
Log.d("Counter", "recompose")),观察调用次数。 - @Stable 注解:对于自定义类,标记
@Stable可以帮助 Compose 更准确地判断不变性,减少不必要的重组。
⚠️ 常见错误:状态丢失
错误写法:
错误写法:
val count = mutableStateOf(0)(缺少 remember)。每次重组都会创建新的 MutableState 实例,导致点击按钮时 UI 无变化。务必使用 remember 固定实例。
性能与稳定性优化
| 场景 | 优化措施 |
|---|---|
| 避免不必要的重组 | 将不读取状态的 UI 抽取为单独的 Composable 并保持稳定(使用 @Stable 或传递纯函数)。 |
| 减少状态粒度 | 尽量使用多个 remember { mutableStateOf() } 分别管理独立的状态,而非一个大对象。这样修改单个字段时只会触发该状态相关的 UI 重组。 |
使用 derivedStateOf |
当状态需要通过其他状态计算而来,且计算代价较高时,使用 val derived by remember { derivedStateOf { … } } 避免每次重组都重新计算。 |
使用 snapshotFlow |
当需要以 Flow 的形式观察状态变化并执行副作用(如网络请求)时,使用 snapshotFlow { state }。 |
| 避免在重组中创建新对象 | remember 中创建的 lambda、列表等也会被记住,但如果依赖 key 变化,可能频繁重建。确保 key 稳定。 |
🎯 本章小结: remember + mutableStateOf 是 Compose 状态管理的基石。
- 创建可观察状态 →
mutableStateOf - 保持状态跨重组 →
remember - 读取时自动订阅,修改时触发重组
- 关注调试手段与常见坑,为后续支付页面的复杂状态管理打下基础