如何启动协程
在kotlinx.coroutines 库中,您可以使用launch 或async 函数启动新的协程。
从概念上讲,async 就像launch。它启动一个单独的协程,它是一个轻量级线程,可与所有其他协程同时工作。
不同之处在于,launch 返回一个 Job 并且不携带任何结果值,而 async 返回一个 Deferred - 一个轻量级的非阻塞未来,表示承诺稍后提供结果。您可以在延迟值上使用.await() 来获得其最终结果,但Deferred 也是Job,因此您可以在需要时取消它。
协程上下文
在Android中我们通常使用两种上下文:
-
uiContext 将执行调度到 Android 主线程 UI 线程(对于父协程)。
-
bgContext 在后台线程中调度执行(对于子协程)。
例子
//dispatches execution onto the Android main UI thread
private val uiContext: CoroutineContext = UI
//represents a common pool of shared threads as the coroutine dispatcher
private val bgContext: CoroutineContext = CommonPool
在下面的示例中,我们将使用CommonPool 表示bgContext,它将并行运行的线程数限制为Runtime.getRuntime.availableProcessors()-1 的值。所以如果协程任务被调度了,但是所有的核都被占用了,就会排队。
您可能需要考虑使用newFixedThreadPoolContext 或您自己的缓存线程池实现。
启动 + 异步(执行任务)
private fun loadData() = launch(uiContext) {
view.showLoading() // ui thread
val task = async(bgContext) { dataProvider.loadData("Task") }
val result = task.await() // non ui thread, suspend until finished
view.showData(result) // ui thread
}
launch + async + async(顺序执行两个任务)
注意:task1和task2是顺序执行的。
private fun loadData() = launch(uiContext) {
view.showLoading() // ui thread
// non ui thread, suspend until task is finished
val result1 = async(bgContext) { dataProvider.loadData("Task 1") }.await()
// non ui thread, suspend until task is finished
val result2 = async(bgContext) { dataProvider.loadData("Task 2") }.await()
val result = "$result1 $result2" // ui thread
view.showData(result) // ui thread
}
launch + async + async(并行执行两个任务)
注意:task1 和 task2 是并行执行的。
private fun loadData() = launch(uiContext) {
view.showLoading() // ui thread
val task1 = async(bgContext) { dataProvider.loadData("Task 1") }
val task2 = async(bgContext) { dataProvider.loadData("Task 2") }
val result = "${task1.await()} ${task2.await()}" // non ui thread, suspend until finished
view.showData(result) // ui thread
}
如何取消协程
函数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() // ui thread
val task = async(bgContext) { dataProvider.loadData("Task") }
val result = task.await() // non ui thread, suspend until finished
view.showData(result) // ui thread
}
完整答案见我的文章Android Coroutine Recipes