[英]How to update UI in coroutines in Kotlin 1.3
I'm trying to call an API and when my variables are ready, update UI components respectively.我正在尝试调用 API,当我的变量准备好时,分别更新 UI 组件。
This is my Network singleton who is launching the coroutine:这是我正在启动协程的网络单身人士:
object MapNetwork {
fun getRoute(request: RoutesRequest,
success: ((response: RoutesResponse) -> Unit)?,
fail: ((throwable: Throwable) -> Unit)? = null) {
val call = ApiClient.getInterface().getRoute(request.getURL())
GlobalScope.launch(Dispatchers.Default, CoroutineStart.DEFAULT, null, {
try {
success?.invoke(call.await())
} catch (t: Throwable) {
fail?.invoke(t)
}
})
}
}
And this is how I call it:这就是我所说的:
network.getRoute(request,
success = {
// Make Some UI updates
},
fail = {
// handle the exception
})
And I get the Exception that says can't update UI from any thread other than UI thread:而且我收到异常,表示无法从 UI 线程以外的任何线程更新 UI:
com.google.maps.api.android.lib6.common.apiexception.c: Not on the main thread
I already tried this solution but resume
in Continuation<T>
class is "deprecated" since Kotlin 1.3我已经尝试过这个解决方案,但自 Kotlin 1.3 起,
Continuation<T>
类中的resume
已“弃用”
To answer your immediate question, you must simply launch the coroutine in the correct context:要回答您的直接问题,您必须简单地在正确的上下文中启动协程:
val call = ApiClient.getInterface().getRoute(request.getURL())
GlobalScope.launch(Dispatchers.Main) {
try {
success?.invoke(call.await())
} catch (t: Throwable) {
fail?.invoke(t)
}
}
However, this would be just the tip of the iceberg because your approach is the wrong way to use coroutines.但是,这只是冰山一角,因为您的方法是使用协程的错误方法。 Their key benefit is avoiding callbacks, but you're re-introducing them.
它们的主要好处是避免回调,但您正在重新引入它们。 You are also infringing on the structured concurrency best practice by using the
GlobalScope
which is not meant for production use.您还通过使用非生产用途的
GlobalScope
侵犯了结构化并发最佳实践。
Apparently you already have an async API that gives you a Deferred<RoutesResponse>
that you can await
on.显然,您已经有了一个异步 API,它为您提供了一个可以
await
的Deferred<RoutesResponse>
。 The way to use it is as follows:使用方法如下:
scope.launch {
val resp = ApiClient.getInterface().getRoute(request.getURL()).await()
updateGui(resp)
}
You may be distressed by the fact that I'm suggesting to have a launch
block in every GUI callback where you must execute suspendable code, but that is actually the recommended way to use this feature.您可能会因为我建议在每个 GUI 回调中都有一个
launch
块而感到苦恼,您必须在其中执行可挂起的代码,但这实际上是使用此功能的推荐方式。 It is in a strict parallel to writing Thread { ... my code ... }.start()
because the contents of your launch
block will run concurrently to the code outside it.它与编写
Thread { ... my code ... }.start()
严格并行,因为launch
块的内容将与其外部的代码同时运行。
The above syntax assumes you have a scope
variable ready which implements CoroutineScope
.上面的语法假设你已经准备好一个实现
CoroutineScope
的scope
变量。 For example, it can be your Activity
:例如,它可以是您的
Activity
:
class MyActivity : AppCompatActivity(), CoroutineScope by MainScope {
override fun onDestroy() {
super.onDestroy()
cancel()
}
}
The MainScope
delegate sets the default coroutine dispatcher to Dispatchers.Main
. MainScope
委托将默认协程调度程序设置为Dispatchers.Main
。 This allows you to use the plain launch { ... }
syntax.这允许您使用普通的
launch { ... }
语法。
private var viewModelJob = Job()
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
uiScope.launch {
withContext(Dispatchers.IO) {
//Do background tasks...
withContext(Dispatchers.Main){
//Update UI
}
}
}
If you're using coroutines-android you can use Dispatchers.Main
如果你使用 coroutines-android 你可以使用
Dispatchers.Main
(gradle dependency is implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0"
) (gradle 依赖是
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0"
)
network.getRoute(request,
success = {
withContext(Dispatchers.Main) {
// update UI here
}
},
fail = {
// handle the exception
})
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.