java.lang.StackOverflowError: 栈大小8MB在androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd
java.lang.StackOverflowError: stack size 8MB at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd
ID: android/compose-recomposition-infinite-loop
版本兼容性
| 版本 | 状态 | 引入 | 弃用 | 备注 |
|---|---|---|---|---|
| Compose BOM 2024.01.00 | active | — | — | — |
| Compose Compiler 1.5.8 | active | — | — | — |
| Kotlin 1.9.22 | active | — | — | — |
| Android 14 (API 34) | active | — | — | — |
根因分析
Jetpack Compose中无限重组循环,由于状态变量在组合期间被更新(例如,在没有正确键的LaunchedEffect中调用setState,或触发重新渲染的可组合lambda中)。
English
Infinite recomposition loop in Jetpack Compose due to a state variable being updated during composition (e.g., calling setState inside a LaunchedEffect without proper key or in a composable lambda that triggers re-render).
官方文档
https://developer.android.com/jetpack/compose/state#side-effects-in-compose解决方案
-
Move the state update outside of composition by using LaunchedEffect with a stable key. Example: LaunchedEffect(Unit) { delay(100); myState.value = newValue } instead of LaunchedEffect(myState.value) { ... } -
Use remember with a derivedStateOf to compute values without triggering recomposition. Example: val derivedValue by remember { derivedStateOf { computeExpensive(myState.value) } } -
Add a snapshotFlow block to observe state changes outside composition. Example: LaunchedEffect(Unit) { snapshotFlow { myState.value }.collect { /* update other state */ } }
无效尝试
常见但无效的做法:
-
Increase stack size via AndroidManifest android:largeHeap="true"
95% 失败
largeHeap increases heap memory, not stack size. Stack size is fixed per thread in Android (8MB default). This does not fix the infinite loop.
-
Wrap the state update in withFrameMillis or withFrameNanos
80% 失败
These APIs schedule work on the next frame but do not prevent the recomposition loop if the state is still updated during composition.