Дождитесь результата LiveData в сопрограмме Kotlin - PullRequest
0 голосов
/ 19 июня 2020

У меня есть класс репозитория с асинхронным методом, возвращающим User, завернутый в LiveData:

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

В сопроводительной области ViewModel я хочу дождаться результата метода getUser() и используйте экземпляр User.

это то, что я ищу:

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

Я не смог найти метод расширения LiveData<>.await() и любые попытки его реализовать. Поэтому, прежде чем делать это самому, я задаюсь вопросом, может быть, есть способ лучше?

Все решения, которые я нашел, касались создания метода getUser() a suspend, но что, если я не могу изменить Repository?

Ответы [ 2 ]

2 голосов
/ 19 июня 2020

Вы должны иметь возможность создать функцию расширения await(), используя suspendCancellableCoroutine(). Вероятно, это не совсем правильно, но что-то вроде этого должно сработать:

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) {
          continuation.resume(value)
          removeObserver(this)
        }
      }

      observeForever(observer)

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

Это должно вернуть первое значение, выданное LiveData, не оставляя наблюдателя позади.

0 голосов
/ 22 августа 2020

это функция расширения, которая соответствует вашим потребностям, эта функция также включает параметр максимального времени ожидания.

fun <T> LiveData<T>.getOrAwaitValue(
    time: Long = 2,
    timeUnit: TimeUnit = TimeUnit.SECONDS,
    afterObserve: () -> Unit = {}
): T {
    var data: T? = null
    val latch = CountDownLatch(1)
    val observer = object : Observer<T> {
        override fun onChanged(o: T?) {
            data = o
            latch.countDown()
            this@getOrAwaitValue.removeObserver(this)
        }
    }
    this.observeForever(observer)

    afterObserve.invoke()

    // Don't wait indefinitely if the LiveData is not set.
    if (!latch.await(time, timeUnit)) {
        this.removeObserver(observer)
        throw TimeoutException("LiveData value was never set.")
    }

    @Suppress("UNCHECKED_CAST")
    return data as T
}
...