Jetpack Compose 的状态管理是其响应式 UI 的核心。简单来说,状态就是任何可以随时间变化的值,而 Compose 的核心任务就是当状态变化时,自动更新界面(这个过程称为“重组”)。
核心概念:状态与重组
在 Compose 中,UI 是状态的函数:UI = f(state)。你不再需要像在传统 View 系统中那样手动调用 setText() 来更新界面,而是改变状态,Compose 会自动完成更新。
- 状态(State):应用的任何可变数据,比如一个数字、一个类实例、或一个列表的滚动位置。
-
重组(Recomposition):当状态发生变化时,Compose 会重新执行读取了该状态的
@Composable函数,以反映出最新状态。这个过程是智能且高效的,只会更新需要变化的部分。
如何在可组合项中创建和管理状态
在 @Composable 函数中,我们使用 mutableStateOf 和 remember 来创建可被 Compose 追踪的状态。
-
mutableStateOf:这个函数会创建一个可观察的MutableState对象。当它的value发生变化时,所有读取这个值的可组合函数都会被自动调度进行重组。 -
remember:由于重组时会重新执行函数,如果没有remember,状态值会被重置。remember可以将值存储在内存中,确保在重组后值仍然保留。
三种等价的声明方式:
// 方式一:直接操作 .value
val name = remember { mutableStateOf("") }
TextField(value = name.value, onValueChange = { name.value = it })
// 方式二:使用 by 委托(最常用,代码简洁)
var name by remember { mutableStateOf("") }
TextField(value = name, onValueChange = { name = it })
// 方式三:解构声明
val (name, setName) = remember { mutableStateOf("") }
使用
by委托需要导入import androidx.compose.runtime.getValue和setValue。
配置更改后的状态保留:rememberSaveable
remember 无法在屏幕旋转等配置更改后保留状态。这时应使用 rememberSaveable,它能利用 Android 的 Bundle 机制自动保存和恢复状态。
// 屏幕旋转后,name 的值依然保留
var name by rememberSaveable { mutableStateOf("") }
对于非基础类型的数据,可以使用 @Parcelize 注解,或通过自定义 Saver(如 mapSaver、listSaver)来保存。
架构模式:状态提升与单向数据流
为了实现可复用、可测试的组件,Compose 推荐使用状态提升模式,将状态移到调用方,使组件变为无状态。
- 有状态 (Stateful):组件自己管理状态,对于简单的、自包含的逻辑很方便,但复用和测试较困难。
-
无状态 (Stateless):组件不持有任何状态,所有数据通过参数传入,事件通过回调(如
onClick)传出。这是更推荐的模式,因为它职责清晰、可复用性高。
状态提升示例:
// 无状态组件:只负责显示和发送事件
@Composable
fun HelloContent(name: String, onNameChange: (String) -> Unit) {
TextField(value = name, onValueChange = onNameChange)
}
// 有状态容器:管理状态并传递给无状态组件
@Composable
fun HelloScreen() {
var name by rememberSaveable { mutableStateOf("") }
HelloContent(name = name, onNameChange = { name = it })
}
这个模式被称为单向数据流:状态(数据)从上(HelloScreen)向下(HelloContent)传递,事件(行为)从下向上传递。
与其他数据流的集成
Compose 可以轻松与 Flow、LiveData、RxJava 等常见的响应式数据源集成,只需将它们转换为 State 即可。
-
collectAsStateWithLifecycle():在 Android 中,推荐使用此 API 以生命周期感知的方式从Flow收集数据,更加节省资源。 -
collectAsState():用于跨平台通用代码,从Flow收集数据。 -
observeAsState():用于将LiveData转换为State。
性能优化:在正确的阶段读取状态
Compose 渲染一帧分为组合 (Composition)、布局 (Layout) 和绘制 (Drawing) 三个阶段。为了优化性能,我们应该尽可能在布局或绘制阶段读取状态,避免不必要的重组。
例如,对于滚动偏移,在 Modifier.offset { } 的 lambda 中读取状态,Compose 就能跳过组合阶段,直接进入布局阶段,性能更好。
总结
-
核心公式:
UI = f(state) -
基础工具:在可组合项中用
remember { mutableStateOf(initialValue) }创建状态。 -
数据持久化:用
rememberSaveable应对配置更改。 - 架构设计:优先采用状态提升和单向数据流模式。
-
数据源集成:使用
collectAsStateWithLifecycle()等 API 连接Flow/LiveData。