Not sure if this is the right channel, but I'm try...
# coroutines
k
Not sure if this is the right channel, but I'm trying to load multiple images (50+) from files as a list and I'm wondering what the best approach is to do it asynchronously and to update the UI. So the flow being • Find the file • Get the input stream • download the image So my assumption is to launch a coroutine for every image separately, do all the actions in that coroutine and then update a flow somehow? Not sure the best method to make sure they're done in parallel and nothing breaks Also I don't want to use a library like Coil
u
What kind of ui do you want to update? Imperative (classical View) or Reactive (Compose)?
k
compose
u
And do you intend having 50 mutable states? or an array of 50+ images in your mutable state?
And do you show them all at once? Or some kind of lazy grid/column/row?
k
an array of 50+ images. Yea like a lazy grid basically, basically viewing a directory of images
u
but all should be loaded immediately? not triggered by the lazy in grid?
k
It would be like the google photos app, where it shows a large amount at a time
u
It is an interesting exercise. You could do it with flatMapMerge. But actually I suggest to use Coil. It will take the fun out of the exercise by providing AsyncImage.
But you need coil 3, which is currently available as rc1.
k
I'm trying to pull images from a local network drive, would coil work with that?
u
i suppose so. try simple path or file-url
k
I'll try that thanks
u
Just realized, Coil.2.7.0 (stable) seems to also work when you are strictly on android (no multi platform)
Here comes an untested Idea for hand made loading. Coil with lazy grid will be a lot more memory efficient as it will only load images when needed. On the down side, you might have visible loading time with Coil when new images are scrolled into the viewport:
Copy code
data class ImageLoadingRequest(val fileName: String, val onLoad: (Image) -> Unit)

fun CoroutineScope.loadImagesAsync(request: Iterable<ImageLoadingRequest>): Job {
    return launch {
        request.asFlow().flatMapMerge(concurrency = DEFAULT_CONCURRENCY) { request ->
            flow {
                // Find the file
                // Get the input stream
                // download the image
                // convert to Image representation

                emit(Pair(convertedImage, request.onLoad))
            }
        }.collect { (image, onLoad) ->
            withContext(Dispatchers.Main) {
                onLoad(image)
            }
        }
    }
}
Consider this a sketch.In the real world you will need nicer data structures, error handling, …
Then in your viewModel:
Copy code
private fun updateImage(index: Int, loadedImage: Image) {
    mutableViewState.update { viewState ->
        val updatedImageList = viewState.images.mapIndexed { i, originalImage ->
            if (i == index) loadedImage else originalImage
        }
        viewState.copy(images = updatedImageList)
    }
}
    
val requests = filenames.mapIndexed { index, fileName ->
    ImageLoadingRequest(fileName) { loadedImage ->
        updateImage(index, loadedImage)
    }
}
loadImagesAsync(requests)
Any success?
k
I was able to get it working with a custom coil Fetcher. Thanks for the suggestion
❤️ 1