这个问题很简单,但是我似乎真的无法理解它。我在IO作用域中启动了一个非阻塞线程,以便从文件中读取。然而,在我从方法返回之前,我不能及时得到结果-它总是返回初始空值";我遗漏了什么?
private fun getFileContents(): String {
var result = ""
val fileName = getFilename()
val job = CoroutineScope(Dispatchers.IO).launch {
kotlin.runCatching {
val file = getFile(fileName)
file.openFileInput().use { inputStream ->
result = String(inputStream.readBytes(), Charsets.UTF_8)
}
}
}
return result
}
协程是异步启动的。非挂起函数不能在不阻塞的情况下等待结果。关于为什么异步代码会导致函数返回默认结果的更多信息,请阅读这里的答案。
getFileContents()
必须是一个挂起函数,以便能够在不阻塞的情况下返回一些东西,在这种情况下,您也不需要启动协程。但是无论调用这个函数的是什么,都必须在挂起函数或协程中。
private suspend fun getFileContents(): String = withContext(Dispatchers.IO) {
val fileName = getFilename()
kotlin.runCatching {
val file = getFile(fileName)
file.openFileInput().use { inputStream ->
result = String(inputStream.readBytes(), Charsets.UTF_8)
}
}.getOrDefault("")
}
有两个"世界"代码:你要么在挂起/协程上下文中,要么不在。当你在一个不是挂起函数的函数中,你只能返回可以立即计算的结果,或者你可以阻塞直到结果准备好。
一般来说,如果你使用协程,你可以在代码的某个高层启动一个协程,然后你可以自由地在任何地方使用挂起函数,因为几乎所有的代码最初都是由协程触发的。所谓"高级",我指的是当UI屏幕出现或UI按钮被按下时启动协同程序。
基本上,你的协程启动通常在UI监听器和UI事件函数中,而不是在像你问题中的函数这样的低级代码中。协程调用一个挂起函数,该挂起函数可以调用其他挂起函数,因此您不需要启动更多的协程来执行各种顺序任务。
另一种解决方案是返回结果的Deferred
,如下所示:
private fun getFileContents(): Deferred<String> {
val fileName = getFilename()
return CoroutineScope(Dispatchers.IO).async {
kotlin.runCatching {
val file = getFile(fileName)
file.openFileInput().use { inputStream ->
result = String(inputStream.readBytes(), Charsets.UTF_8)
}
}.getOrDefault("")
}
}
但是要解压缩结果,您需要在协程中的某个地方调用Deferred实例上的await()
。