Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for LiveData result in a Kotlin coroutine

I have a repository class with an asynchronous method returning User wrapped into a LiveData:

interface Repository {
    fun getUser(): LiveData<User>
}

In a ViewModel's coruotine scope I want to wait for a result of getUser() method and use an User instance.

this is what, I am looking for:

private fun process() = viewModelScope.launch {
   val user = repository.getUser().await()
   // do something with a user instance
}

I could not find LiveData<>.await() extension method, and any attempts to implement it. So before doing it my self, I wonder maybe there is some better way?

All solutions that I have found were about making getUser() a suspend method, but what if I can not change Repository?

like image 894
nsk Avatar asked Jun 19 '20 16:06

nsk


People also ask

What are advanced coroutines with Kotlin flow and livedata?

You can know more about advanced coroutines with Kotlin Flow and LiveData and include the logical aspect of your code in a LiveData builder. Kotlin Coroutines 1. 2. 0 comes up with a cold stream called Flow. Kotlin Flow is used for carrying out asynchronous operations.

How to perform asynchronous operations using Kotlin coroutines?

By combining Kotlin Coroutines Flow, LiveData and Data Binding you can perform asynchronous operations.

How can I use Kotlin to improve my livedata activity?

Use Kotlin to simplify my code by being more concise and focus on (isolated) minimal tasks. I’ll first use a simple ViewModel with some LiveData objects that the Activity will be able to observe. Here is how MainActivity uses it: I’m also okay in most cases with Google’s demo code on ViewModel:

How do I wait for a coroutine to finish?

To wait for a coroutine to finish, you can call Job.join. join is a suspending function, meaning that the coroutine calling it will be suspended until it is told to resume. At the point of suspension, the executing thread is released to any other available coroutines (that are sharing that thread or thread pool).


1 Answers

You should be able to create an await() extension function using suspendCancellableCoroutine(). This probably is not exactly correct, but something along these lines should work:

public suspend fun <T> LiveData<T>.await(): T {
  return withContext(Dispatchers.Main.immediate) {
    suspendCancellableCoroutine { continuation ->
      val observer = object : Observer<T> {
        override fun onChanged(value: T) {
          removeObserver(this)
          continuation.resume(value)
        }
      }

      observeForever(observer)

      continuation.invokeOnCancellation {
        removeObserver(observer)
      }
    }
  }
}

This should return the first value emitted by the LiveData, without leaving an observer behind.

like image 188
CommonsWare Avatar answered Nov 15 '22 03:11

CommonsWare