Kotlin:如何执行异步功能并等待其完成?

时间:2019-06-25 23:56:59

标签: android kotlin

我正在Kotlin中编写我的第一个应用程序,所以我对此很陌生。它执行的功能之一是从API读取内容,然后根据结果更新屏幕。

我已经用协同程序尝试了很多东西,但是似乎没有任何作用。

例如,我有这样的东西:

private fun readAPI() {
    runBlocking {
        fun rAPI() = async {
            val api = "..."
            result = URL(api).readText()
        }
        println(tag, "Result: " + rAPI().await())
    }
}

还有许多不同的方法。似乎没有任何作用。在上述情况下,我收到一个异常“ android.os.NetworkOnMainThreadException”。

到目前为止,唯一有效的解决方案是使用OkHttp3进行操作,如下所述:https://rstopup.com/como-hacer-una-solicitud-a-la-api-de-kotlin.html(它是西班牙语,但您会明白的),并且它起作用了,它带来了API响应,我解析它,填写我的sqlite3数据库,依此类推。但是,由于我不知道API何时结束,因此无法更新屏幕控件。而且,如果我尝试串行执行此操作,则会得到一个例外,即只有启动该活动的线程才是可以更新该活动或类似内容的线程。

我已经看到并遵循了很多有关暂停功能,启动等的教程,并且他们尝试使用delay()来模仿API调用,这些教程运行良好,直到我尝试做一个真正的API调用。

那么,您能指出一个完整的示例,说明如何使用Kotlin调用API,然后更新一些屏幕元素吗?

编辑 我正在编辑通过val改变乐趣:

runBlocking {
  val rAPI = async {
    val api = "..."
    URL(api).readText()
  }
  Log.w(tag, rAPI.await())
}

我遇到了“ android.os.NetworkOnMainThreadException”异常。

2 个答案:

答案 0 :(得分:1)

由于您要使用coroutine-async的方式,因此必须告诉主线程等待您。您需要使用suspend函数或块来执行此操作。

GlobalScope.launch {
    suspend {
        Log.d("coroutineScope", "#runs on ${Thread.currentThread().name}")
        delay(10000)
        withContext(Dispatchers.Main) {
            Log.d("coroutineScope", "#runs on ${Thread.currentThread().name}")
        }
    }.invoke()
}

结果日志

09:36:09.500 D/: #runs on DefaultDispatcher-worker-1
// 10 seconds later
09:36:19.678 D/: #runs on main 

我认为这应该可以解决问题。

但是我建议您通过向其中传递回调(使用onSuccess和onFail或其他方法)来了解如何使用OkHttp / Volley。 或将Retrofit2RxJava一起使用,以解决许多此类问题。

编辑 对于Module with the Main dispatcher had failed to initialize错误,将withContext()行替换为此

withContext(Handler(Looper.getMainLooper()).asCoroutineDispatcher())

答案 1 :(得分:0)

在Kotlin中,您可以按照本代码实验室https://codelabs.developers.google.com/codelabs/kotlin-coroutines/#6

中的说明将回调转换为协程。