简体   繁体   中英

Combine data from Kotlin Flow/LiveData

I've got a flow from my repository that looks something like this:

val userListFlow: Flow<List<User>> = channelFlow<List<User>> {
        source.setOnUserUpdatedListener { userList ->
           trySend(userList)
        }
        awaitClose {
            logger.info("waitClose")
            source.setOnUserUpdatedListener(null)
        }
    }.stateIn(
       scope = externalScope,
       started = SharingStarted.WhileSubscribed(5000),
       initialValue = emptyList()
   )


 suspend fun getUserThumbnail(user: User): File {
    return getUserThumbFromCache(user) ?: run {
       fetchUserThumbnailRemote()
    }
 } 

 private suspend fetchUserThumbnailRemote(user: User): Bitmap {
    thumbnailService.getUserThumbnailBitmap(user.id)
 }

 fun getUserThumbFromCache(user: User) {
    val thumbFile = getThumbFile(user)
    return if (thumbFile.exists() && thumbFile.size() > 0) {
      thumbFile
    } else null
 }

 private fun getThumbFile(user: User): File {
   return File(cacheDir, "${user.id}.jpg")
 }
}

For each of these users I can call the suspend function to get a thumbnail for the user. I don't want to wait for the thumbnail before showing the list of users though, I'd rather it show the users and then when the thumbnail is fetched, update the list.

However I'd like the list to be updated when a thumbnail is fetched..

From my ViewModel I have something like

  data class UserWithThumb(user: User, thumb: File?)


  val userLiveData = repo.userListFlow.map {
      UserWithThumb(it, repo.getUserThumbFromCache(it))
  }.asLiveData()

So then from my Fragment I do

 viewModel.userLiveData.observe(viewLifecycleOwner) {
       userListAdapter.submitList(it)
 }

My thumbnails are all null though as I need to fetch them from remote. However if I call that function then that will delay my list from getting to the UI until the thumbnail is fetched. How can I get the thumbnail to the UI in a clean way? I realize that I need to have my livedata or flow update itself once the thumbnail is fetched but I have no idea how to hook that into my code. Any ideas would be appreciated.

I suppose one way to think about this is I'd like my upstream (repository) flow to contain the list of users but then I'd like to update the list given to the view not just when the upstream (repo) flow gets new data but when new thumbnails are downloaded as well..

What I understood from the question is, you have a list of UserWithThumb that is created once you set User s list and you want to show it to the UI immediately. In the background you want to fetch User thumbnails and once you receive them, you want to update the list again.

One way to achieve what you want is:

val userLiveData = flow {
    repo.userListFlow.collect { users ->
        val initialList = users.map { UserWithThumb(it, repo. getUserThumbFromCache(it)) }
        emit(initialList)
        coroutineScope {
            val finalList = users.map {
                async(Dispatchers.IO) {  // fetch all thumbnails in parallel
                    UserWithThumb(it, repo. getUserThumbnail(it))
                }
            }.awaitAll() // wait until all thumbnails have been fetched
            emit(finalList)
        } 
    }
}.asLiveData()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM