I am getting a fatal exception in `painterResource...
# compose
a
I am getting a fatal exception in
painterResource()
in a composable where the id for the resource is provided as an argument for the composable and ultimately got from a view model's
StateFlow
property. The id can be one of several dozen, and it always fails on the same one but only if it comes after another specific one, otherwise everything is fine, maybe I am misusing the API somehow? More details in the thread.
This is the point of failure (the painterResource line):
Copy code
if (subdivisionsPicture != null) {
                 val painter = painterResource(subdivisionsPicture)
                    Image(painter = painter, contentDescription = null)
                }
The composable containing this code gets the
subdivisionsPicture
argument value from a flow, collected like this:
Copy code
val subPicture = vm.subdivisionsPicture.collectAsState()
And passed like this:
Copy code
subdivisionsPicture = subPicture.value,
This is the exception:
Copy code
2021-09-24 18:12:31.159 E: FATAL EXCEPTION: main @coroutine#143
    Process: com.bandlab.bandlab.test, PID: 23413
    java.lang.IndexOutOfBoundsException: Index: 3, Size: 3
        (Coroutine boundary)
        at androidx.compose.runtime.PausableMonotonicFrameClock.withFrameNanos(PausableMonotonicFrameClock.kt:63)
        at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2.invokeSuspend(Recomposer.kt:416)
        at androidx.compose.runtime.Recomposer$recompositionRunner$2$2.invokeSuspend(Recomposer.kt:673)
        at androidx.compose.runtime.Recomposer$recompositionRunner$2.invokeSuspend(Recomposer.kt:672)
        at androidx.compose.ui.platform.WindowRecomposer_androidKt$createLifecycleAwareViewTreeRecomposer$2$onStateChanged$1.invokeSuspend(WindowRecomposer.android.kt:271)
     Caused by: java.lang.IndexOutOfBoundsException: Index: 3, Size: 3
        at java.util.ArrayList.get(ArrayList.java:437)
        at androidx.compose.ui.graphics.vector.GroupComponent.move(Vector.kt:490)
        at androidx.compose.ui.graphics.vector.VectorApplier.move(VectorCompose.kt:114)
        at androidx.compose.runtime.ComposerImpl$realizeMovement$2.invoke(Composer.kt:2909)
        at androidx.compose.runtime.ComposerImpl$realizeMovement$2.invoke(Composer.kt:2909)
        at androidx.compose.runtime.CompositionImpl.applyChanges(Composition.kt:629)
        at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:733)
        at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release(Composer.kt:2980)
        at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:432)
        at androidx.compose.ui.graphics.vector.VectorPainter.composeVector(VectorPainter.kt:157)
        at androidx.compose.ui.graphics.vector.VectorPainter.RenderVector$ui_release(VectorPainter.kt:177)
        at androidx.compose.ui.graphics.vector.VectorPainterKt.rememberVectorPainter-mlNsNFs(VectorPainter.kt:83)
        at androidx.compose.ui.graphics.vector.VectorPainterKt.rememberVectorPainter(VectorPainter.kt:106)
        at androidx.compose.ui.res.PainterResources_androidKt.painterResource(PainterResources.android.kt:67)
        at com.bandlab.metronome.tool.MetronomeToolViewKt$SubdivisionControls$1$1$1.invoke(MetronomeToolView.kt:369)
        at com.bandlab.metronome.tool.MetronomeToolViewKt$SubdivisionControls$1$1$1.invoke(MetronomeToolView.kt:346)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116)
        at androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$1.invoke(ComposableLambda.jvm.kt:127)
        at androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$1.invoke(ComposableLambda.jvm.kt:127)
        at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:140)
        at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2156)
        at androidx.compose.runtime.ComposerImpl.skipToGroupEnd(Composer.kt:2422)
        at androidx.compose.material.ButtonKt.Button(Button.kt:134)
        at com.bandlab.metronome.tool.MetronomeToolViewKt.SubdivisionControls(MetronomeToolView.kt:335)
        at com.bandlab.metronome.tool.MetronomeToolViewKt.MetronomeToolView(MetronomeToolView.kt:142)
        at com.bandlab.metronome.tool.MetronomeToolViewKt$MetronomeToolView$2.invoke(Unknown Source:6)
        at com.bandlab.metronome.tool.MetronomeToolViewKt$MetronomeToolView$2.invoke(Unknown Source:10)
        at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:140)
        at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2156)
        at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:2399)
        at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2580)
        at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2573)
        at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(SnapshotState.kt:540)
        at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:2566)
        at androidx.compose.runtime.ComposerImpl.recompose$runtime_release(Composer.kt:2542)
        at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:613)
🐛 1
And for what it's worth this ☝️ is the drawable (which leads to the exception but only coming after another one)
a
This looks worth filing on the issue tracker, cc @Nader Jawad
n
Yes please file a ticket and include the vector asset and sample composable that reproduces the issue. Thanks!
Which version of compose are you running? I just pulled down that asset and it seemed to parse and render fine:
a
By the sounds of it and the stack trace, it might have something to do with changing the underlying asset in a recomposition. Might need the before/after assets that led to the crash?
a
Hello, yes I also believe it has to do with going from a specific drawable to another one I'll open an issue and provide both!
I came up with a minimal example that reproduces the issue (somehow). Activity:
Copy code
class TestActivity: AppCompatActivity() {

    val id = MutableStateFlow(R.drawable.div4_3_101)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent { TestView(this) }
    }

    fun click() {
        if (id.value == R.drawable.div4_3_101) {
            id.value = R.drawable.div4_3_001
        } else {
            id.value = R.drawable.div4_3_101
        }
    }
}
Composable:
Copy code
@Composable
fun TestView(act: TestActivity) {
    Column {
        Button(onClick = act::click) { Text("TEST") }
        val state = act.id.collectAsState()
        val id = state.value
        val painter = painterResource(id)
        Image(painter = painter, contentDescription = null)
    }
}
Interestingly, the app won't crash when clicking the button the first time, the second drawable (div4_3_001) is displayed but I see a one-error message in logcat:
Copy code
2021-09-27 11:47:53.482 E: Task exception on worker thread: java.lang.IndexOutOfBoundsException: Index:2, Size:2
After that, clicking the button again has no effect (the image's drawable doesn't change anymore). Using compose 1.0.2, with
kotlinCompilerExtensionVersion "1.0.1"
Here the two drawables used in the example:
I opened an issue on the tracker: https://issuetracker.google.com/issues/201233897 In the meanwhile I am working around the issue by loading the drawable on the vievmodel side and emitting it through a StateFlow.
133 Views