coil-kt / coil

Image loading for Android and Compose Multiplatform.
https://coil-kt.github.io/coil/
Apache License 2.0
10.63k stars 647 forks source link

java.lang.IllegalStateException: Reading a state that was created after the snapshot was taken or in a snapshot that has not yet been applied #2368

Open fanqieVip opened 1 month ago

fanqieVip commented 1 month ago

After upgrade from version 2.4 to 2.6 this crash starts to occur on Android (tested on 14): java.lang.IllegalStateException: Reading a state that was created after the snapshot was taken or in a snapshot that has not yet been applied at androidx.compose.runtime.snapshots.SnapshotKt.readError(Snapshot.kt:1929) at androidx.compose.runtime.snapshots.SnapshotKt.readable(Snapshot.kt:1913) at androidx.compose.runtime.SnapshotMutableStateImpl.getValue(SnapshotState.kt:135) at coil.compose.AsyncImagePainter.getRequest(AsyncImagePainter.kt:487) at coil.compose.AsyncImagePainter$onRemembered$1$1.invoke(AsyncImagePainter.kt:305) at coil.compose.AsyncImagePainter$onRemembered$1$1.invoke(AsyncImagePainter.kt:305) at androidx.compose.runtime.SnapshotStateKtSnapshotFlowKt$snapshotFlow$1.invokeSuspend(SnapshotFlow.kt:184) at androidx.compose.runtime.SnapshotStateKtSnapshotFlowKt$snapshotFlow$1.invoke(Unknown Source:8) at androidx.compose.runtime.SnapshotStateKt__SnapshotFlowKt$snapshotFlow$1.invoke(Unknown Source:4) at kotlinx.coroutines.flow.SafeFlow.collectSafely(Builders.kt:57) at kotlinx.coroutines.flow.AbstractFlow.collect(Flow.kt:226) at kotlinx.coroutines.flow.internal.ChannelFlowTransformLatest$flowCollect$3.invokeSuspend(Merge.kt:23) at kotlinx.coroutines.flow.internal.ChannelFlowTransformLatest$flowCollect$3.invoke(Unknown Source:8) at kotlinx.coroutines.flow.internal.ChannelFlowTransformLatest$flowCollect$3.invoke(Unknown Source:4) at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:61) at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:261) at kotlinx.coroutines.flow.internal.ChannelFlowTransformLatest.flowCollect(Merge.kt:21) at kotlinx.coroutines.flow.internal.ChannelFlowOperator.collectTo$suspendImpl(ChannelFlow.kt:153) at kotlinx.coroutines.flow.internal.ChannelFlowOperator.collectTo(Unknown Source:0) at kotlinx.coroutines.flow.internal.ChannelFlow$collectToFun$1.invokeSuspend(ChannelFlow.kt:56) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.internal.DispatchedContinuation.resumeWith(DispatchedContinuation.kt:200) at kotlin.coroutines.ContinuationKt.startCoroutine(Continuation.kt:129) at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:89) at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:123) at kotlinx.coroutines.channels.ProduceKt.produce(Produce.kt:129) at kotlinx.coroutines.channels.ProduceKt.produce$default(Produce.kt:117) at kotlinx.coroutines.flow.internal.ChannelFlow.produceImpl(ChannelFlow.kt:115) at kotlinx.coroutines.flow.internal.ChannelFlow$collect$2.invokeSuspend(ChannelFlow.kt:119) at kotlinx.coroutines.flow.internal.ChannelFlow$collect$2.invoke(Unknown Source:8) at kotlinx.coroutines.flow.internal.ChannelFlow$collect$2.invoke(Unknown Source:4) at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:61) at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:261) at kotlinx.coroutines.flow.internal.ChannelFlow.collect$suspendImpl(ChannelFlow.kt:118) at kotlinx.coroutines.flow.internal.ChannelFlow.collect(Unknown Source:0) at kotlinx.coroutines.flow.internal.ChannelFlowOperator.collect$suspendImpl(ChannelFlow.kt:169) at kotlinx.coroutines.flow.internal.ChannelFlowOperator.collect(Unknown Source:0) at coil.compose.AsyncImagePainter$onRemembered$1.invokeSuspend(AsyncImagePainter.kt:307) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104) at android.os.Handler.handleCallback(Handler.java:1013) at android.os.Handler.dispatchMessage(Handler.java:101) at android.os.Looper.loopOnce(Looper.java:226) at android.os.Looper.loop(Looper.java:328) at android.app.ActivityThread.main(ActivityThread.java:9210) at java.lang.reflect.Method.invoke(Native Method)

colinrtwhite commented 1 month ago

Do you know how I can reproduce this? I'm not able to reproduce this locally in Coil 2.6. I recently fixed the same issue in 3.0.0-alpha08, but didn't think it affected the 2.x branch.

fanqieVip commented 1 month ago

您知道我该如何重现这个问题吗?我无法在 Coil 2.6 中本地重现这个问题。我最近在 中修复了同样的问题3.0.0-alpha08,但没想到它影响了 2.x 分支。

The following code is a banner style that contains images. When I use pageState.animateScrollToPage to automatically switch, this fault occurs. Later, before calling pageState.animateScrollToPage, I first called withContext (Dispatchers. Main) to switch to the main thread, and this fault never happened again

@Composable fun Banner( modifier: Modifier = Modifier, actualCount: Int, loopDelay: Long = 3000, loopPeriod: Long = 3000, displayStatus: LiveData, pageContent: @Composable (page: Int) -> Unit ) { if (actualCount <= 0) { return } val pageState = rememberPagerState(initialPage = 0) { actualCount } if (actualCount > 1) { val coroutineScope = rememberCoroutineScope() DisposableEffect(Unit) { val timer: Job = coroutineScope.launch(Dispatchers.Default) { delay(loopDelay) while (isActive) { withContext(Dispatchers.Main) { if (displayStatus.value == DisplayStatus.RESUME) { pageState.animateScrollToPage((pageState.currentPage + 1) % actualCount) } } delay(loopPeriod) } } onDispose { timer.cancel() } } } else { LaunchedEffect(Unit) { pageState.scrollToPage(pageState.currentPage) } } HorizontalPager( state = pageState, modifier = Modifier .fillMaxWidth() .wrapContentHeight() .then(modifier), userScrollEnabled = actualCount > 1, pageSpacing = 1.dp ) { index -> pageContent(index) } }