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 Bitmap
s 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