[英]Wait for LiveData result in a Kotlin coroutine
我有一个存储库 class ,其中异步方法返回User
包装到LiveData
:
interface Repository {
fun getUser(): LiveData<User>
}
在 ViewModel 的协程 scope 中,我想等待getUser()
方法的结果并使用User
实例。
这就是我正在寻找的:
private fun process() = viewModelScope.launch {
val user = repository.getUser().await()
// do something with a user instance
}
我找不到LiveData<>.await()
扩展方法,以及任何实现它的尝试。 所以在我自己做之前,我想知道也许有更好的方法?
我发现的所有解决方案都是关于使getUser()
成为suspend
方法,但是如果我不能更改Repository
怎么办?
您应该能够使用suspendCancellableCoroutine()
创建一个await()
扩展 function 。 这可能不完全正确,但沿着这些思路应该可以工作:
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
发出的第一个值,而不会留下观察者。
这是一个适合您需要的扩展 function,这个 function 还包括最大等待时间参数。
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
}
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) }
}
}
}
您实际上可以“等待”协程中的实时数据
这是一种解决方法,可能有更好的选择:
suspend fun retrieveUser(): LiveData<User> {
val liveUser: MutableLiveData<User> = MutableLiveData()
var counter = 0
val timeOut = 20 // 2 sec
while(liveUser.value.isNullOrEmpty()) {
if(counter > timeout) break
counter++
delay(100)
}
return liveUser
真正的 Kotlin 方法是更改存储库接口并使 getUser() 成为挂起方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.