android
runtime_error
ai_generated
true
java.lang.StackOverflowError: stack size 8MB at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd
ID: android/compose-recomposition-infinite-loop
86%Fix Rate
83%Confidence
1Evidence
2023-11-20First Seen
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| Compose BOM 2024.01.00 | active | — | — | — |
| Compose Compiler 1.5.8 | active | — | — | — |
| Kotlin 1.9.22 | active | — | — | — |
| Android 14 (API 34) | active | — | — | — |
Root Cause
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).
generic中文
Jetpack Compose中无限重组循环,由于状态变量在组合期间被更新(例如,在没有正确键的LaunchedEffect中调用setState,或触发重新渲染的可组合lambda中)。
Official Documentation
https://developer.android.com/jetpack/compose/state#side-effects-in-composeWorkarounds
-
90% success 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) { ... }
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) { ... } -
85% success Use remember with a derivedStateOf to compute values without triggering recomposition. Example: val derivedValue by remember { derivedStateOf { computeExpensive(myState.value) } }
Use remember with a derivedStateOf to compute values without triggering recomposition. Example: val derivedValue by remember { derivedStateOf { computeExpensive(myState.value) } } -
88% success Add a snapshotFlow block to observe state changes outside composition. Example: LaunchedEffect(Unit) { snapshotFlow { myState.value }.collect { /* update other state */ } }
Add a snapshotFlow block to observe state changes outside composition. Example: LaunchedEffect(Unit) { snapshotFlow { myState.value }.collect { /* update other state */ } }
中文步骤
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 */ } }
Dead Ends
Common approaches that don't work:
-
Increase stack size via AndroidManifest android:largeHeap="true"
95% fail
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% fail
These APIs schedule work on the next frame but do not prevent the recomposition loop if the state is still updated during composition.