vico
vico copied to clipboard
Couple crashes with `java.util.ConcurrentModificationException` and `java.util.NoSuchElementException: Key null is missing in the map`
How to reproduce
Hey! Thanks again for the library. It's been working great so far, but we've started getting a couple crashes now after pushing it to more users (nothing major yet).
I've tried to reproduce it for the last couple days, but haven't managed to.
I have a LazyColumn
with a CartesianChartHost
. This is the basic gist of the chart:
ProvideVicoTheme(rememberM2VicoTheme()) {
val modelProducer = remember { CartesianChartModelProducer.build() }
LaunchedEffect(entries) {
modelProducer.tryRunTransaction {
lineSeries { series(x = entries.map { it.key }, y = entries.map { it.amount }) }
}
}
// ...
CartesianChartHost(
modifier = modifier,
scrollState = rememberVicoScrollState(scrollEnabled = false),
modelProducer = modelProducer,
diffAnimationSpec = spring(stiffness = Spring.StiffnessVeryLow),
chart = rememberCartesianChart(
rememberLineCartesianLayer(
lines = listOf(rememberLineSpec(shader = shader, backgroundShader = null)),
axisValueOverrider = remember {
object : AxisValueOverrider {
override fun getMinY(minY: Float, maxY: Float, extraStore: ExtraStore): Float {
return (minY - 0.0005f).coerceAtLeast(0f)
}
override fun getMaxY(minY: Float, maxY: Float, extraStore: ExtraStore): Float {
return (maxY + 0.0005f).coerceAtLeast(0.1f)
}
}
},
),
decorations = listOf(
rememberHorizontalLine(
y = { state.start },
line = rememberLineComponent(
color = MaterialTheme.colors.gray80, shape = remember { DashedShape() },
),
),
),
persistentMarkers = // Custom logic to display some markers
),
marker = primaryMarker,
markerVisibilityListener = // Custom logic for the marker visibility
)
}
I have removed a few bits for brevity and because I can not share the whole code. Let me know if any of it is important to figuring this issue.
The chart is only shown if there are entries to display (entries
is not empty). It seems to happen after the user scrolls to the bottom of the screen (chart is at the top).
Could it be because the CartesianChartModelProducer
is recreated when the chart is visible again? Since it's in a LazyColumn
, the chart leaves the composition and then returns once the user sees it again. Not sure it should matter though, since the chart is also a "new" one.
I'm not entirely sure if it is an issue on our end or an issue in Vico's end. Do you folks have any idea of how we can try to replicate this issue?
Let me know if you see any issue in our implementation too, please!
Observed behavior
There are a few crashes. I have a couple stack traces, not sure if they're linked to the same issue or not
Fatal Exception: java.util.ConcurrentModificationException:
at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:760)
at java.util.LinkedHashMap$LinkedValueIterator.next(LinkedHashMap.java:787)
at kotlin.collections.CollectionsKt___CollectionsKt.mapTo(CollectionsKt___Collections.kt:1620)
at com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer.tryUpdate(CartesianChartModelProducer.kt:59)
at com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer.access$tryUpdate(CartesianChartModelProducer.kt:38)
at com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer$Transaction.tryCommit(CartesianChartModelProducer.kt:206)
at com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer.tryRunTransaction(CartesianChartModelProducer.kt:171)
at com.getbux.android.stocks.ui.chart.ValueChartKt$ValueChart$1$1.invokeSuspend(ValueChart.kt:55)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
at androidx.compose.ui.platform.AndroidUiDispatcher.performTrampolineDispatch(AndroidUiDispatcher.android.kt:81)
at androidx.compose.ui.platform.AndroidUiDispatcher.access$performTrampolineDispatch(AndroidUiDispatcher.android.kt:41)
at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.run(AndroidUiDispatcher.android.kt:57)
at android.os.Handler.handleCallback(Handler.java:958)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:224)
at android.os.Looper.loop(Looper.java:318)
at android.app.ActivityThread.main(ActivityThread.java:8677)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:561)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1013)
and
Fatal Exception: java.util.NoSuchElementException: Key null is missing in the map.
at kotlin.collections.MapsKt__MapWithDefaultKt.getOrImplicitDefaultNullable(MapsKt__MapWithDefault.kt:24)
at kotlin.collections.MapsKt__MapsKt.getValue(MapsKt__Maps.kt:360)
at com.patrykandpatrick.vico.core.cartesian.data.MutableChartValuesKt$toImmutable$1.getYRange(MutableChartValues.kt:122)
at com.patrykandpatrick.vico.core.cartesian.layer.LineCartesianLayer.toDrawingModel(LineCartesianLayer.kt:598)
at com.patrykandpatrick.vico.core.cartesian.layer.LineCartesianLayer.prepareForTransformation(LineCartesianLayer.kt:583)
at com.patrykandpatrick.vico.core.cartesian.layer.LineCartesianLayer.prepareForTransformation(LineCartesianLayer.kt:73)
at com.patrykandpatrick.vico.core.cartesian.CartesianChart$transformationPreparationModelAndLayerConsumer$1.invoke(CartesianChart.java:106)
at com.patrykandpatrick.vico.core.cartesian.CartesianChart.consume(CartesianChart.kt:328)
at com.patrykandpatrick.vico.core.cartesian.CartesianChart.forEachWithLayer(CartesianChart.kt:316)
at com.patrykandpatrick.vico.core.cartesian.CartesianChart.prepareForTransformation(CartesianChart.kt:251)
at com.patrykandpatrick.vico.compose.cartesian.CartesianChartModelProducerKt$collectAsState$2$1$2.invoke(CartesianChartModelProducer.kt:124)
at com.patrykandpatrick.vico.compose.cartesian.CartesianChartModelProducerKt$collectAsState$2$1$2.invoke(CartesianChartModelProducer.kt:124)
at com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer$UpdateReceiver.handleUpdate(CartesianChartModelProducer.kt:229)
at com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer.registerForUpdates(CartesianChartModelProducer.kt:146)
at com.patrykandpatrick.vico.compose.cartesian.CartesianChartModelProducerKt$collectAsState$2$1.invokeSuspend(CartesianChartModelProducer.kt:114)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.java:585)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:802)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:706)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:693)
Expected behavior
The app should not crash
Vico version(s)
2.0.0-alpha.20
Android version(s)
12, 13, 14
Additional information
We're actually on 2.0.0-alpha.19
, but there seem to be only small changes in alpha.20
.
It's quite hard to figure out where the issue is coming from. Since the stack trace does not point to our app code at all, we don't know if it's an issue with our integration or an internal Vico issue.
And since we're not familiar with Vico's codebase, it's a bit harder to try to replicate it. I've tried scrolling up and down multiple times, adding delays in multiple places, adding more charts to the screen and scrolling, configuration changes, going to the background/foreground, spamming buttons that cause the entries to change, navigating back or to another screen, and more. Let me know if you have any ideas that could replicate it, any help would be great here!
Thanks in advance