State 与 MutableState 基础:remember 与 mutableStateOf 创建可观察状态

原理:Compose 的声明式状态驱动重组

在 Jetpack Compose 中,UI 不是手动更新的,而是通过 状态 驱动的。当状态发生改变时,Compose 框架会自动重组(Recompose)该状态所关联的 Composable 函数,从而更新 UI。这一机制的核心是 可观察状态(Observable State)。

MutableState 是 Compose 中最基础的观察容器,它持有一个可变的值,并在值变化时通知 Compose 框架。

remember 函数用于保持对象跨重组(Recomposition)的存在性;没有 remember 包裹的状态会在每次重组时被重新创建,从而丢失变化。

实现流程:从创建到触发重组

  1. 在 Composable 函数内通过 mutableStateOf() 创建初始状态(返回 MutableState)。
  2. 用 remember 将状态包裹,避免在每次重组时被重建。
  3. 读取 value 属性(或使用 Kotlin 委托 by)来驱动 UI。
  4. 修改 value 时,Compose 检测到变化,自动触发包含该 state 读取的 Composable 重组。
💡 委托技巧: 使用 by 委托后的变量可以直接读写,例如 var count by remember { mutableStateOf(0) },代码更简洁。
State → Recomposition 流程图 remember { mutableStateOf } UI 读取 value 用户/事件修改 value Compose 检测变化 触发 Composable 重组 👆 重组范围:只有读取该 state 的 Composable 及其直接子 Composable 会被重组

核心代码示例:计数器

@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
  • 读取时自动订阅,修改时触发重组
  • 关注调试手段与常见坑,为后续支付页面的复杂状态管理打下基础