Kamel-Media / Kamel

Kotlin asynchronous media loading and caching library for Compose.
Apache License 2.0
595 stars 23 forks source link

Lazy Column Item Performance issue #39

Closed Polenoso closed 1 year ago

Polenoso commented 1 year ago

Using Compose Multiplatform ->

When using asyncPainterResource within a lazy colum or lazy row, the performance is broken under Android app, not happening in iOS App.

It seems the issue is related to the resource being held by the thread trying to download and paint the image.

We've tried to define a coroutine scope context specifically for each asyncPainterResource but the issue is still there.

Let us share the code we're testing:

@Composable
fun ArtWorkList(artworks: List<ArtWorkData>, modifier: Modifier = Modifier, isLoading: Boolean, onPagination: CoroutineScope.() -> Unit) {
    val lazyColumnState = rememberLazyListState()
    val shouldPaginate by remember { derivedStateOf {
        (isLoading && lazyColumnState.layoutInfo.totalItemsCount > 0 && (lazyColumnState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: -9) >= artworks.size - 1 && lazyColumnState.firstVisibleItemIndex != 0)
    } }

    LazyColumn(horizontalAlignment = Alignment.CenterHorizontally, state = lazyColumnState) {
        items(artworks) {artwork ->
            Card(modifier = Modifier.padding(6.dp), elevation = 4.dp) {
                Column {
                    ArtWorkImage(artwork.downloadUrl, modifier = Modifier.height(194.dp))
                    Text("By: ${artwork.author}", style = MaterialTheme.typography.h4)
                }
            }
        }
        item {
            if (isLoading) {
                LinearProgressIndicator()
            }
        }
    }
    LaunchedEffect(shouldPaginate, onPagination)
}

@Composable
fun ArtWorkImage(value: String, modifier: Modifier = Modifier) {
    val scope = rememberCoroutineScope()
    val resource = asyncPainterResource(data = Url(value), key = value) {
        coroutineContext = scope.coroutineContext
    }
    Box(modifier = modifier.border(BorderStroke(1.dp, Color.Black)), contentAlignment = Alignment.Center) {
        KamelImage(
            resource = resource,
            contentDescription = value,
            onLoading = { progress -> CircularProgressIndicator(progress) },
            onFailure = {
                Text(it.toString())
            }
        )
    }
}

@Serializable
data class ArtWorkData(
    val id: String,
    val author: String,
    val url: String,
    @SerialName("download_url")
    val downloadUrl: String
)

Even though the code is handling pagination, the issue happens without pagination.

Attaching a video where the app freezes without pagination enabled.

https://github.com/Kamel-Media/Kamel/assets/47319719/d01351b7-6320-4f32-ad15-46398d72a070

luca992 commented 1 year ago

I'm not sure how to reproduce your example. If you can update the logic to load data I can take a look.

Polenoso commented 1 year ago

I'm not sure how to reproduce your example. If you can update the logic to load data I can take a look.

Hi, let me share the repository with you:

https://github.com/Polenoso/compose-multiplatform-tutorial

There you can find everything to test it.

Thanks

anshulupadhyay03 commented 1 year ago

@luca992 @Polenoso I am having a similar issue running the app on Android. Additionally, I have observed using the memory profiler tool that the memory is also not getting released, and the RAM increasing drastically. check out this video : https://youtu.be/v78eyXcYU7Q

luca992 commented 1 year ago

Try @youranshul 0.6.1-SNAPSHOT, it has a performance fix to load images in a IO sub-scope of the composable's CoroutineScope. Now the asyn image loading coroutine will actually cancel when it goes off screen like it should. and if that doesn't help you are probably doing too much work on the main thread somewhere else like @Polenoso

@Polenoso your repo had a bunch of non kamel performance issues. Made a pr and explained further: https://github.com/Polenoso/compose-multiplatform-tutorial/pull/1 plus I updated it with the new snapshot which I think definitely did help. I lowered the filter quality too.... Those images are pretty massive however. I don't think you'll ever get amazing performance

Polenoso commented 1 year ago

@luca992 thanks!

Absolutely, I'm initiating to kotlin and compose so I knew it couldn't be just related to kamel, but it seemed that something could be happening behind the scenes that's why I opened the issue. Thanks for the help.

anshulupadhyay03 commented 1 year ago

@luca992 thanks for the fix but My build is failing when I sync it with media.kamel:kamel-image:0.6.1 . This is my repo : https://github.com/youranshul/KmmMovieBuff

luca992 commented 1 year ago

@luca992 thanks for the fix but My build is failing when I sync it with media.kamel:kamel-image:0.6.1 . This is my repo : https://github.com/youranshul/KmmMovieBuff

tried it... That's not a kamel issue. This is your issue

implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0") {
    version {
        strictly("1.7.0")
    }
}