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

问题描述 投票:7回答:2

我正在尝试调用API,当我的变量准备就绪时,分别更新UI组件。

这是我的网络单身人士正在启动协程:

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

我得到的异常表示无法从UI线程以外的任何线程更新UI:

com.google.maps.api.android.lib6.common.apiexception.c: Not on the main thread

我已经尝试过this解决方案,但resume类中的Continuation<T>因为Kotlin 1.3而被“弃用”

android kotlin ui-thread kotlin-coroutines
2个回答
8
投票

要回答您的直接问题,您必须在正确的上下文中启动协程:

val call = ApiClient.getInterface().getRoute(request.getURL())
GlobalScope.launch(Dispatchers.Main) {
    try {
        success?.invoke(call.await())
    } catch (t: Throwable) {
        fail?.invoke(t)
    }
}

然而,这只是冰山一角,因为你的方法是使用协同程序的错误方法。他们的主要好处是避免回调,但你正在重新引入它们。您还使用不适合生产的structured concurrency侵犯了GlobalScope最佳实践。

显然你已经有了一个异步API,可以为你提供Deferred<RoutesResponse>,你可以await。使用方法如下:

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

您可能会对我建议在每个GUI回调中都有launch块的事实感到苦恼,其中您必须执行可挂起的代码,但实际上这是使用此功能的推荐方法。它与编写Thread { ... my code ... }.start()严格平行,因为launch块的内容将同时运行到它之外的代码。

上面的语法假设你有一个scope变量ready,它实现了CoroutineScope。例如,它可以是你的Activity

class MyActivity : AppCompatActivity(), CoroutineScope {
    lateinit var masterJob: Job
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + masterJob

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        masterJob = Job()
    }

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

请注意coroutineContext如何将默认协程调度程序设置为Dispatchers.Main。这允许您使用普通的launch { ... }语法。


6
投票

如果你正在使用coroutines-android,你可以使用Dispatchers.Main (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
        }) 
© www.soinside.com 2019 - 2024. All rights reserved.