如何启动一个协程
在 kotlinx.coroutines
库中,您可以使用 launch
或 async
函数启动新的协程。
从概念上讲,async
与 launch
相似。它启动一个独立的协程,这是一个轻量级线程,与所有其他协程并发工作。
区别在于,launch
返回一个 Job
,不带任何结果值,而 async
返回一个 Deferred
- 一个轻量级非阻塞 future,表示稍后提供结果的承诺。 您可以在延迟值上使用 .await()
来获取其最终结果,但是 Deferred
也是一个 Job
,因此如果需要,可以取消它。
协程上下文
在 Android 中,我们通常使用两个上下文:
uiContext
将执行调度到 Android 主线程 UI
线程 (对于父协程)。
bgContext
将执行调度到后台线程 (对于子协程)。
示例
private val uiContext: CoroutineContext = UI
private val bgContext: CoroutineContext = CommonPool
在以下示例中,我们将使用CommonPool
作为bgContext
,它将并行运行的线程数限制为Runtime.getRuntime.availableProcessors()-1
的值。因此,如果协程任务已安排但所有核心都已占用,则它将被排队。
您可能需要考虑使用newFixedThreadPoolContext
或自己实现缓存线程池。
launch + async (执行任务)
private fun loadData() = launch(uiContext) {
view.showLoading()
val task = async(bgContext) { dataProvider.loadData("Task") }
val result = task.await()
view.showData(result)
}
启动 + 异步 + 异步 (依次执行两个任务)
注:task1 和 task2 会依次执行。
private fun loadData() = launch(uiContext) {
view.showLoading()
val result1 = async(bgContext) { dataProvider.loadData("Task 1") }.await()
val result2 = async(bgContext) { dataProvider.loadData("Task 2") }.await()
val result = "$result1 $result2"
view.showData(result)
}
启动 + 异步 + 异步(并行执行两个任务)
注意:task1 和 task2 会并行执行。
private fun loadData() = launch(uiContext) {
view.showLoading()
val task1 = async(bgContext) { dataProvider.loadData("Task 1") }
val task2 = async(bgContext) { dataProvider.loadData("Task 2") }
val result = "${task1.await()} ${task2.await()}"
view.showData(result)
}
如何取消一个协程
loadData
函数返回一个 Job
对象,可以被取消。当父协程被取消时,它的所有子协程也会被递归地取消。
如果在 dataProvider.loadData
仍在进行中时调用了 stopPresenting
函数,则函数 view.showData
将永远不会被调用。
var job: Job? = null
fun startPresenting() {
job = loadData()
}
fun stopPresenting() {
job?.cancel()
}
private fun loadData() = launch(uiContext) {
view.showLoading()
val task = async(bgContext) { dataProvider.loadData("Task") }
val result = task.await()
view.showData(result)
}
完整答案可在我的文章“Android Coroutine Recipes”中找到。