I'm trying to call an API and when my variables are ready, update UI components respectively.

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)
            }

        })
    }
}

我这样称呼它:

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:

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

推荐答案

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)
    }
}

然而,这只是冰山一角,因为您的方法使用协程的方式是错误的.它们的主要好处是避免了回调,但您正在重新引入它们.您使用GlobalScope也侵犯了structured concurrency最佳实践,而GlobalScope不打算用于生产.

Apparently you already have an async API that gives you a Deferred<RoutesResponse> that you can await on. The way to use it is as follows:

scope.launch {
    val resp = ApiClient.getInterface().getRoute(request.getURL()).await()
    updateGui(resp)
}

你可能会因为我建议在你必须执行可挂起代码的每个图形用户界面回调中都有一个launchGUIlaunch挡路而苦恼,但这实际上是推荐的使用这个特性的方式.它与编写Thread { ... my code ... }.start()是严格平行的,因为launch挡路的内容将与其外部的代码并发运行.

上面的语法假设您有一个scope变量,它实现CoroutineScope.例如,它可以是您的Activity:

class MyActivity : AppCompatActivity(), CoroutineScope by MainScope {

    override fun onDestroy() {
        super.onDestroy()
        cancel()
    }
}

The MainScope delegate sets the default coroutine dispatcher to Dispatchers.Main. This allows you to use the plain launch { ... } syntax.

Kotlin相关问答推荐

如何防止Android Studio在每一行的选项卡凹痕和文本之间添加空白?

在Kotlin中可以连接两个范围吗?

Gradle Jooq配置自定义生成器

在KMM合成多平台中创建特定于平台的视图

使用另一个对象的列表创建对象

Scala性状线性化等价于Kotlin

在Kotlin项目中使用Free Fair AspectJ插件(使用Gradle)

相当于roomdb中的DateTime Bigint列的是什么

为什么使用 return instance ?: synchronized(this) { instance ?: PreferenceParameterState(context) } 时无法获得单例?

如何在 Kotlin 中为类方法调用传递变量

Kotlin 中的as Long和.toLong()有什么区别?

在子类中覆盖 kotlin 运算符扩展函数

为什么 Kotlin 在 sumOf 函数 lambda 中默认不将数字视为Int?

TestContainers PostgreSQLContainer 与 Kotlin 单元测试:Not enough information to infer type variable SELF

如何有效地填充 Gradle Kotlin DSL 中的额外属性?

无法从 XML 访问 NavHostFragment

Kotlin 语言是如何用 Kotlin 编写的?

runInTransaction 块内的挂起方法

Kotlin val difference getter override vs assignment

修改扩展函数中的this