Coroutine Builder — 是 Kotlin Coroutines 的函数,用于创建和启动协程,并确定其执行方式。launch、async、runBlocking 和 produce 构建器覆盖了不同的场景:从后台任务到带结果返回的并行计算。根据 JetBrains, 2024 的数据,Coroutine Builder 是协程模型的基础,提供了结构化并发和生命周期管理。
要点
Coroutine Builder — 是一个 Kotlin 扩展函数,它接受 CoroutineScope 和 suspend 块,创建并启动一个新的协程。每个构建器确定协程的执行方式:带或不带结果返回,通过阻塞线程或异步方式。构建器是语言协程模型的入口点。
所有构建器通过 CoroutineScope 工作,后者管理子协程的生命周期。当 scope 被取消时,通过它启动的所有协程都会被自动取消 — 这是结构化并发的原则。这种方法防止了协程泄漏,并保证了可预测的完成。
import kotlinx.coroutines.*
fun main() = runBlocking {
// 构建器在 CoroutineScope 内运行
val job = launch {
delay(1000L)
println("世界!")
}
println("你好,")
job.join()
}
Kotlin 提供了四个内置的协程构建器:launch、async、runBlocking 和 produce。每个都有自己的返回类型和应用领域。对于 Android 移动开发,主要的是 launch 和 async — 它们以非阻塞方式工作,并与架构组件集成。
| 构建器 | 返回类型 | 线程阻塞 | 场景 |
|---|---|---|---|
| launch | Job | 否 | Fire-and-forget 任务 |
| async | Deferred<T> | 否 | 并行计算 |
| runBlocking | T | 是 | 测试、main 函数 |
| produce | ReceiveChannel<E> | 否 | 流式传输(已废弃) |
每个构建器接受额外的参数:CoroutineStart(启动策略)、CoroutineContext(调度器、异常)和命名的代码块。默认情况下,协程立即启动(CoroutineStart.DEFAULT)。
launch — 在 Android 开发中最常用的构建器。它启动一个不返回结果的协程,并返回一个 Job 对象来管理其生命周期。这是需要副作用的操作的理想选择:保存到数据库、发送分析数据、更新 UI。
launch 构建器接受 CoroutineScope、可选的 CoroutineContext 和 suspend 块。返回的 Job 允许取消协程、等待其完成或检查状态。
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
val job: Job = scope.launch(CoroutineStart.LAZY) {
val data = fetchFromNetwork()
saveToDatabase(data)
}
job.start()
job.join()
CoroutineStart.LAZY 参数将启动延迟到显式调用 start() 或 join()。这对于延迟初始化和条件启动很有用。对于标准的立即启动,使用 CoroutineStart.DEFAULT 或省略参数。
async — 返回 Deferred<T> 的构建器 — 结果的异步承诺。await() 调用会暂停协程直到获得结果,而不会阻塞线程。这是 Kotlin 协程中并行任务的主要机制。
当需要同时执行多个独立操作时,async 特别有效。与顺序调用 suspend 函数不同,async 并行启动协程,从而缩短总执行时间。
suspend fun fetchUserData(): UserData {
val deferred1 = CoroutineScope(Dispatchers.IO).async { api.getProfile() }
val deferred2 = CoroutineScope(Dispatchers.IO).async { api.getSettings() }
val deferred3 = CoroutineScope(Dispatchers.IO).async { api.getNotifications() }
return UserData(
profile = deferred1.await(),
settings = deferred2.await(),
notifications = deferred3.await()
)
}
Deferred 继承自 Job,因此 async 支持所有生命周期操作:取消、等待完成、异常处理。当 scope 被取消时,子 Deferred 协程会自动被取消。
runBlocking — 是唯一一个阻塞当前线程直到协程完成的构建器。它创建一个新的 CoroutineScope 并启动传递的协程,同时阻塞调用线程。用于 main() 入口点、测试以及与 blocking 代码的集成。
runBlocking 在三种场景下是合理的:应用程序入口点(main)、suspend 函数的单元测试以及与基于回调的库集成(无法使用 suspend)。在 Android 生产代码中,在主线程上使用 runBlocking 强烈不建议。
class CoroutineTest {
@Test
fun `test suspend function`() = runBlocking {
val result = mySuspendFunction()
assertEquals("expected", result)
}
}
对于测试,建议使用 kotlinx-coroutines-test 与 TestCoroutineDispatcher 代替 runBlocking — 这提供了时间控制,并避免了测试环境中的阻塞。
Coroutine Builder 的选择取决于返回结果和执行场景。如果操作不需要返回数据 — 使用 launch。如果需要异步操作的结果 — async。runBlocking 仅用于 bridging,而 produce 应替换为 Flow 以用于响应式流。
在使用 Kotlin Coroutines 的 Android 项目中,主要的构建器对是 launch 和 async。launch 在 ViewModel 和 UseCases 中用于启动协程,而 async 用于对网络或数据库的并行请求。现代库(Ktor、Room)已经支持 suspend 函数,这最大限度地减少了直接使用 async 的需求。
常见问题
launch 返回 Job 且不返回执行结果,而 async 返回 Deferred<T> — 一个可以从中通过 await() 获取结果的对象。launch 用于 fire-and-forget 操作,async 用于返回数据的任务。
不建议。runBlocking 在主线程上会导致 ANR 并阻塞 UI。在 Activity 和 Fragment 内部使用 lifecycleScope.launch — 这是无阻塞的内置解决方案。
launch 构建器返回一个 Job 对象,允许您控制协程的生命周期:取消(cancel)、等待完成(join)、检查状态(isActive、isCompleted、isCancelled)。
Deferred<T> — 是 async 构建器返回的异步结果承诺。它继承自 Job,并添加了用于获取结果的 await() 方法、用于非阻塞访问的 getCompleted() 方法和用于检查异常的 getCompletionExceptionOrNull() 方法。
使用 CoroutineStart.LAZY 参数:scope.launch(start = CoroutineStart.LAZY) { ... }。然后调用 job.start() 或 job.join() 进行实际启动。这对于延迟初始化和协程的条件执行很有用。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。