How to subscribe to update data in Item LazyColumn when using task list

43 Views Asked by At

I'm fetching a list of images from the server and displaying them in a LazyColumn. However, the images can be quite large, up to 5md, and I need to show them in their entirety.

I can't use Coil because the server, powered by Amazon, generates a unique URL.

To address this, I cache the images in the device's memory and display them. Initially, I considered loading them all at once by calling the file download function in each LaunchedEffect within every LazyColumn item, and returning the path to the image as a callback. However, I encountered an error:

java.lang.OutOfMemoryError: Failed to allocate a 16-byte allocation with 199,440 free bytes and 194KB until OOM, target footprint 201326592, growth limit 201326592; giving up on allocation because <1% of heap free after GC.

So, I devised an alternative approach, downloading images sequentially in a coroutine. This helped me eliminate the error and device freezing issues.

However, I'm unsure how to correctly subscribe to updates in my ItemPhoto so that it knows when an image with ID "1" has been cached and should be displayed within the LazyColumn.

Here's my code. I have written it in one file and it completely shows the functionality of what will be on the screenshot.

    @HiltViewModel
class BigFileViewModel  @Inject constructor(
    val serviceApi : ServiceApi
) : ViewModel(){
    private val channel = Channel<DownloadImage>()
    var loadedImagesList  = mutableStateOf<List<DownloadImage>?>(null)
        private set
    init{
        CoroutineScope(Dispatchers.Default).launch {
            for (operation in channel) {
                CoroutineScope(Dispatchers.Default).async {
                    serviceApi.downloadFileLink(operation.imageLink).collect{response->
                        if(response is NetworkResponse.Success){
                            updateItem(DownloadImage(operation.itemId, response.data))
                        }
                    }
                }
            }
        }
    }

    private fun updateItem(downloadImage: DownloadImage) {
        loadedImagesList .value = loadedImagesList .value.orEmpty() + downloadImage
    }
    fun downloadPhoto(itemId : Int, url : String){
        viewModelScope.launch {
            channel.send(DownloadImage(itemId, url))
        }
    }
}

@Composable
fun BigFileList(bigFileViewModel : BigFileViewModel = hiltViewModel()){
    LazyColumn(content = {
        items(1){
            FlowRowItem(bigFileViewModel)
        }
    })
}

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun FlowRowItem( bigFileViewModel : BigFileViewModel){

    FlowRow(
        modifier = Modifier
            .padding(8.dp),
        horizontalArrangement = Arrangement.Start
    ) {
        for(item in 0..23){
            ItemPhoto((0..10000).random(), imageList.random(), bigFileViewModel)
        }
    }
}

@Composable
fun ItemPhoto(itemId : Int, setLink : String, bigFileViewModel : BigFileViewModel){
    val link = remember { mutableStateOf<String?>( null ) }
    val updateList by bigFileViewModel.loadedImagesList

    LaunchedEffect(key1 = Unit, block = {
        bigFileViewModel.downloadPhoto(itemId, setLink)
    })

    LaunchedEffect(key1 = updateList, block = {
        updateList?.forEach {item->
            if(item.itemId == itemId){
                link.value = item.imageLink
            }
        }
    })

    link.value?.let {url->
        Image(
            painter = rememberAsyncImagePainter(
                url,
                filterQuality = FilterQuality.None,
            ),
            contentDescription = null,
            modifier = itemModifier2,
            contentScale = ContentScale.Crop
        )
    } ?: run {
        Box(contentAlignment = Alignment.Center) {
           Column(
               modifier = itemModifier2,
               verticalArrangement = Arrangement.spacedBy(5.dp, Alignment.Top),
               horizontalAlignment = Alignment.CenterHorizontally,
           ) {
               CircularProgressIndicator()
               Text("$itemId")
           }
        }
    }
}

enter image description here

0

There are 0 best solutions below