等待LiveData生成Kotlin协程

时间:2020-06-19 16:11:42

标签: android kotlin android-livedata kotlin-coroutines

我有一个带有异步方法的存储库类,该异步方法返回包装在 The provided target group arn:aws:elasticloadbalancing:green/37e9e has target type ip, which is incompatible with the bridge network mode specified in the task definition. "pro-airflow-service" 中的User

LiveData

在ViewModel的常规作用域中,我想等待interface Repository { fun getUser(): LiveData<User> } 方法的结果并使用getUser()实例。

这就是我正在寻找的东西

User

我找不到private fun process() = viewModelScope.launch { val user = repository.getUser().await() // do something with a user instance } 扩展方法,以及任何实现它的尝试。 所以在做我自己之前,我想知道也许还有更好的方法吗?

我发现的所有解决方案都是将LiveData<>.await()设为getUser()方法,但是如果我不能更改suspend怎么办?

4 个答案:

答案 0 :(得分:5)

您应该能够使用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) {
          removeObserver(this)
          continuation.resume(value)
        }
      }

      observeForever(observer)

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

这应该返回LiveData发出的第一个值,而不会留下观察者。

答案 1 :(得分:0)

这是一个适合您需要的扩展功能,该功能还包括最大等待时间参数。

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
}

答案 2 :(得分:0)

真正的Kotlin方法是更改​​Repository接口,并使getUser()成为暂停方法。

答案 3 :(得分:0)

suspend inline fun <T> suspendCoroutineWithTimeout(
    timeout: Long,
    crossinline block: (CancellableContinuation<T>) -> Unit
): T? {
    var finalValue: T? = null
    withTimeoutOrNull(timeout) {
        finalValue = suspendCancellableCoroutine(block = block)
    }
    return finalValue
}

suspend inline fun <T> suspendCoroutineObserverWithTimeout(
    timeout: Long,
    data: LiveData<T>,
    crossinline block: (T) -> Boolean
): T? {
    return suspendCoroutineWithTimeout<T>(timeout) { suspend ->
        var observers : Observer<T>? = null
        val oldData = data.value
         observers = Observer<T> { t ->
             if (oldData == t) {
                 KLog.e("参数一样,直接return")
                 return@Observer
             }
             KLog.e("参数不一样,刷新一波")
            if (block(t) && !suspend.isCancelled) {
                suspend.resume(t)
                observers?.let { data.removeObserver(it) }
            }
        }

        data.observeForever(observers)
        suspend.invokeOnCancellation {
            KLog.e("删除observiers")
            observers.let { data.removeObserver(it) }
        }
    }
}