重组(Recomposition) 是 Jetpack Compose 的核心机制:当 Composable 读取的状态发生变化时,Compose 会重新执行读取了该状态的那部分 Composable 函数,用新的状态重新生成 UI 描述,然后更新界面。
它不是重新创建 Activity/View,也不是整棵树全部重建,而是按需、可跳过地重新执行相关 @Composable 代码。
一、什么叫重组?
Compose 是声明式 UI:
kotlin
UI = f(state)
当 state 变了,Compose 需要重新调用 f 来得到新的 UI。这个"重新调用受影响的 Composable"的过程就叫重组。
例如:
kotlin
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Column {
Text("Count = $count") // 读取了 count
Button(onClick = { count++ }) { // 写入 count
Text("+1")
}
}
}
点击按钮时:
-
count++修改了MutableState。 -
Compose 发现
Text("Count = $count")读取过count。 -
于是标记相关重组作用域为无效。
-
下一帧重新执行相关 Composable。
-
Text显示新的数字。
二、重组的特点
-
按需触发:只重组读取了变化状态的作用域。
-
可以跳过 :如果参数是稳定类型且
equals相等,Compose 可能跳过子 Composable。 -
可能频繁发生:Composable 应尽量幂等、快速、无副作用。
-
不是重新创建 View:Compose 会 diff 新旧节点树,只更新需要更新的部分。
三、怎样触发重组?
核心一句话:
被 Composable 读取的 State 发生变化,就会触发重组。
常见触发方式:
1. 修改 MutableState
kotlin
var count by remember { mutableStateOf(0) }
count++ // 触发
底层是:
kotlin
val count = remember { mutableStateOf(0) }
count.value++ // 触发
2. 修改 Snapshot 集合
kotlin
val list = remember { mutableStateListOf<Int>() }
list.add(1) // 触发读取该 list 的 Composable 重组
类似还有:
kotlin
mutableStateMapOf()
3. StateFlow / LiveData 转成 State
kotlin
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
当 StateFlow 发射新值时,collectAsStateWithLifecycle() 会更新对应的 State,从而触发重组。
LiveData 类似:
kotlin
val data by liveData.observeAsState()
4. 父 Composable 重组,导致子 Composable 参数变化
父级重组时,会重新调用子 Composable。
如果子 Composable 的参数发生变化,或者参数类型不稳定,子 Composable 可能重组。
如果参数是稳定类型且 equals 相等,Compose 可以跳过子 Composable。
5. CompositionLocal 变化
kotlin
CompositionLocalProvider(LocalColor provides newColor) {
// 读取 LocalColor.current 的 Composable 会重组
}
6. remember(key) 的 key 变化
kotlin
remember(key) { ... }
当 key 变化时,remember 会重新计算。
但 key 变化通常也是因为某个 State 变化导致当前 Composable 重组。
四、什么不会触发重组?
- 修改普通变量:
kotlin
var count = 0
count++ // 不会触发 UI 更新
- 修改普通集合:
kotlin
val list = mutableListOf<Int>()
list.add(1) // 不会触发
- 修改了一个 State,但没有任何 Composable 读取它:
kotlin
val state = mutableStateOf(0)
state.value = 1 // 如果没人读取,不会触发 UI 重组
五、总结
-
重组:状态变化后,Compose 重新执行读取了该状态的 Composable,以更新 UI。
-
触发条件 :被 Composable 读取的
State发生变化。 -
常见来源 :
mutableStateOf、mutableStateListOf、collectAsState、CompositionLocal、父级重组导致参数变化等。 -
注意 :Composable 可能被多次执行,不要在 Composable 函数体里直接做网络请求、修改外部变量等副作用;应使用
LaunchedEffect、SideEffect、DisposableEffect等。