Home > Software engineering >  How to release bitmap in jetpack compose
How to release bitmap in jetpack compose

Time:11-14

I'm currently displaying some bitmaps inside a LazyVerticalGrid. To avoid out of memory error, I'm trying to recycle bitmap doing the following:

@Composable
fun ComicsList(covers: List<ComicCover>, onComicClicked: (ComicCover) -> Unit) {
    LazyVerticalGrid(
        columns = GridCells.Fixed(3),
        contentPadding = PaddingValues(16.dp),
        verticalArrangement = spacedBy(8.dp),
        horizontalArrangement = spacedBy(8.dp)
    ) {
        items(
            items = covers,
            key = { it.id }) {
            ComicCoverView(it, onComicClicked)
        }
    }
}

@Composable
fun ComicCoverView(comic: ComicCover, onComicClicked: (ComicCover) -> Unit) {
    Card {
        DisposableEffect(
            Image(
                modifier = Modifier
                    .height(180.dp)
                    .clickable { onComicClicked(comic) },
                bitmap = comic.cover.asImageBitmap(),
                contentDescription = null,
                contentScale = ContentScale.FillHeight,
            )
        ) { onDispose { comic.cover.recycle() } }
    }
}

But I got the following error:

java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@eb4ab61

Any idea on how to properly clean up resources?

CodePudding user response:

There is nothing wrong how you recycle Bitmap but the issue is where you recycle them.

ComicCoverView is enters composition anytime that item is visible or it's next item that is to be visible in scroll direction.

You most likely recycle Bitmaps that you wish to show again, you can verify it by using imageBitmap.asAndroidBitmap().isRecycled before setting imageBitmap.

And where you should recycle Bitmaps is when you are done showing LazyVerticalGrid.

CodePudding user response:

Looks like the bitmap is already loaded in memory in the ComicCover object, so when the ComicCover gets loaded again, during scroll, after onDispose triggered, it will just hold a reference to a recycle bitmap.

Should be more easy if you just set the uri||resource id in the ComicCover and load the bitmap inside the DisposableEffect and recycle it onDispose like you are doing already. This way you'll have a fresh bitmap everytime a specific ComicCover gets composed.

When loading the bitmap everytime might be computationally inefficiet during scroll. To optimize little bit you could add a ProgressIndicator or a Placeholder for the bitmap and then add a delay(600) inside DisposableEffect before you load the bitmap in memory. So when the user scroll the list only the last bitmaps on screen will be loaded.

Moreover, if the bitmaps are small and you are using paging I don't think would be much of a problem if they get preloaded in memory and recycled when changing page

  • Related