https://kotlinlang.org logo
Title
v

Vadim Kapustin

11/18/2020, 11:32 AM
The app displays an icon using
Icon(imageFromResource("images/logo.png"))
But sometimes, when the system is under heavy load I have NullPointerException in DesktopImageAssetKt.loadResource() Apparently the icon doesn't have time to load... How to make waiting for the icon to load and then display it?
i

Igor Demin

11/18/2020, 12:02 PM
imageFromResource
shouldn't be used directly in
@Composable
function. Use
imageResource
instead (it loads resource only in the first call of
Composable
function) But
NullPointerException
looks strange. Loading and composition here is synchronous in a single thread, so theoretically everything should work. Also strange that
Icon
sometimes works.
Icon
shouldn't work with "png" images, it will show black rectangle instead of an image. For "png" images we should use
Image
v

Vadim Kapustin

11/18/2020, 12:33 PM
I check format of logo.png - this is PNG with size 32x31 pixels. And he is normally showed as Icon :)
I get it from old application ico-file and save in some Icon editor as PNG
I changed
imageFromResource
to
imageResource
and tried to repeat situation with
NullPointerException
, all works well...
After some compilations and test executions I receive again same error:
Exception in thread "AWT-EventQueue-0 @coroutine#1" java.lang.NullPointerException
at androidx.compose.ui.graphics.DesktopImageAssetKt.loadResource(DesktopImageAsset.kt:80)
i

Igor Demin

11/18/2020, 2:00 PM
Exception happened in this line?
val resource = Thread.currentThread().contextClassLoader.getResource(path)
Maybe something overrides contextClassLoader in your application 🤔. Is it pure "Compose" application? Also it will be very helpful if you can provide a snippet with bug
v

Vadim Kapustin

11/18/2020, 2:30 PM
I find coroutine launch in my composable. When coroutine is running Icon changes its tint. So maybe it breaks something. I move coroutine launch to outside of Composable and will be wait this bug again...
@Igor Demin So, after many runs, I got this error again. I have little experience in describing errors, and I do not know if I can repeat this error in a separate code... I'll try to give you as much information as possible here: bug.txt - message about bug HintContainer - container for all main window content and hints. Render of main window:
@Composable
override fun render() {
    HintContainer {
        Scaffold(
            topBar = { infoBar.render() }
        ) {
            //...
infoBar render contains StatusIcon:
@Composable
override fun render() {
    val connectionState = remember { databaseController.connectionStatus }.collectAsState()
    TopAppBar {
        val connectMessage = derivedStateOf {
            when (connectionState.value) {
                ConnectionStatus.INITIALIZED -> "Connecting to database"
                ConnectionStatus.SUCCESS -> "Connected to ${databaseController.connection}"
                else -> "Failed connection"
            }
        }
        val statusColor = derivedStateOf {
            if (connectionState.value == ConnectionStatus.SUCCESS) Color.Unspecified
            else AppTheme.colors.material.error
        }

        Row(modifier = Modifier.weight(1f)) {
            StatusIcon(
                message = connectMessage.value,
                color = statusColor.value,
                modifier = Modifier.align(Alignment.CenterVertically).padding(8.dp)
            )
            // .....
connectionStatus described in databaseController:
private val _state = MutableStateFlow<ConnectionStatus>(ConnectionStatus.NOT_REQUESTED)
val connectionStatus: StateFlow<ConnectionStatus> get() = _state
_state.value changes in another coroutine I hope this information will be enough
Exception happened in this line?
val resource = Thread.currentThread().contextClassLoader.getResource(path)
Yes
i

Igor Demin

11/20/2020, 5:01 PM
Thanks! I will look at it
Unfortunately I can't reproduce the bug (everything seems fine). Can you try this function instead of `imageResource`:
@Composable
fun imageResource2(path: String): ImageBitmap {
    return remember(path) { Image.makeFromEncoded(loadResource2(path)).asImageBitmap() }
}

private fun loadResource2(path: String): ByteArray {
    val resource = ClassLoader.getSystemClassLoader().getResource(path)
    requireNotNull(resource) { "Resource $path not found" }
    return resource.readBytes()
}
Also maybe on the latest version (0.2.0-build128) it will be working? We changed default coroutine dispatcher recently (https://github.com/JetBrains/compose-jb/issues/17)
v

Vadim Kapustin

11/23/2020, 6:22 PM
I've been working on the latest version for the last two days and I didn't get this error...
i

Igor Demin

11/23/2020, 6:24 PM
I hope we fixed it 🙂