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

时间:2018-10-31 08:34:16

标签: android kotlin ui-thread kotlin-coroutines

我正在尝试调用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起就被“弃用”

3 个答案:

答案 0 :(得分:8)

要回答您的紧迫问题,您只需在正确的环境中启动协程:

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最佳做法。

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

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

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

以上语法假设您已经准备好一个scope变量,该变量实现了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 { ... }语法。

答案 1 :(得分:7)

private var viewModelJob = Job()
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)

uiScope.launch {
            withContext(Dispatchers.IO) {
                //Do background tasks...
                withContext(Dispatchers.Main){
                    //Update UI
                }
            }
        }

答案 2 :(得分:5)

如果您使用的是协程Android,则可以使用format Indicating the image format. Default:'png' Valid values:'jpeg'|'png'|'webp' quality Number between 0 and 1 indicating image quality. Default:1 circle force the result to be cropped into a circle Valid Values:truefalse
(等级依赖性为Dispatchers.Main

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0"