协程是一种并发设计模式,你可以在 Android 上使用它来简化异步代码。协程是在 Kotlin 1.3 时正式发布的,它吸收了一些其他语言已经成熟的经验。
在 Android 上,协程可用于帮助解决两个主要问题:
- 管理耗时任务,防止它们阻塞主线程
- 提供主线程安全,或从主线程安全地调用网络或磁盘操作
本主题描述如何使用 Kotlin 协程来解决这些问题,让你能够写出更清晰、更简洁的代码。
管理耗时任务
在 Android 上,每个应用都有一个主线程来处理用户界面和管理用户交互。如果你的应用给主线程分配了太多工作,应用可能会变得很卡。网络请求、JSON 解析、读写数据库,甚至只是遍历大型列表,都可能导致应用运行的足够慢,从而导致可见的延迟或直接卡住。这些耗时任务都应该放在主线程之外运行。
下面的例子显示了一个虚构的耗时任务的简单协程实现:
suspend fun fetchDocs() { // Dispatchers.Main
val result = get("https://developer.android.com") // Dispatchers.IO for `get`
show(result) // Dispatchers.Main
}
suspend fun get(url: String) = withContext(Dispatchers.IO) { /* ... */ }
协程通过在常规函数的基础上,添加两个操作符来处理长时间运行的任务。除了调用(invoke or call) 和 返回(return),协程还添加了挂起 (suspend) 和恢复 (resume):
- suspend 挂起当前协程,保存本地变量
- resume 让从一个挂起协程从挂起点恢复执行
你只能从另外一个挂起函数里调用挂起函数,或者使用协程构建器例如 launch 来启动一个新的协程。
在上面的例子中,get() 仍然在主线程运行,但是它会在启动网络请求之前挂起协程。当网络请求完成时,get() 恢复挂起的协程,而不是使用回调来通知主线程。
Kotlin 使用堆栈来管理哪个函数和哪个局部变量一起运行。挂起协程时,将复制当前堆栈帧并保存。当恢复时,堆栈帧将从保存它的位置复制回来,函数将重新开始允许。即使代码看起来像顺序执行的代码会阻塞请求,协程也能确保网络请求不在主线程上。
使用协程确保主线程安全
Kotlin 协程使用调度器来确定哪些线程用于协程执行。要在主线程之外运行代码,可以告诉 Kotlin 协程在 Default 调度器或 IO 调度器上执行工作。在 Kotlin 中,所有协程都必须在调度器中运行,即使它们在主线程上运行。协程可用挂起它们自己,而调