简体   繁体   English

如何在 Kotlin 1.3 的协程中更新 UI

[英]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,它为您提供了一个可以awaitDeferred<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 .上面的语法假设你已经准备好一个实现CoroutineScopescope变量。 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM