launch — Kotlin 中的 Coroutine Builder,它在不返回结果的情况下启动协程,返回一个 Job 对象来管理执行。该构建器实现了 fire-and-forget 模式:协程在后台线程中开始工作并自动结束。根据 JetBrains 文档,2024,launch 是在 Kotlin Coroutines 中启动并行任务的主要方式。
要点
launch — 是一个 Kotlin 扩展函数,通过 import kotlinx.coroutines 可用,它在指定的 CoroutineScope 中创建一个新的协程。与普通函数不同,launch 内的代码是并发执行的——协程可以在不阻塞线程的情况下挂起(suspend)并稍后恢复。
Launch 构建器是 Kotlin 协程模型的基本构建块。它不返回计算结果,但返回一个 Job 对象,允许控制协程的执行。通过 launch 在另一个协程内部启动的所有子协程都绑定到父协程。
Launch 构建器被定义为一个内联函数,带有多个参数:CoroutineScope、CoroutineContext、CoroutineStart 和 suspend 块。参数具有默认值,使得 launch 对典型场景很方便。
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
context 参数接受一个 CoroutineContext,它结合了调度器、异常处理器和命名元素。最常传递的是 Dispatchers.IO、Dispatchers.Main 或用于隔离错误的 SupervisorJob。
Job — 返回的对象,代表协程的生命周期。Job 可以处于以下状态:New、Active、Completing、Completed、Cancelling、Cancelled。每个状态反映执行状态并允许对变化做出反应。
val job = CoroutineScope(Dispatchers.IO).launch {
repeat(10) { i ->
delay(1000L)
println("进度:$i")
}
}
println("Job 处于活动状态:${job.isActive}")
delay(2500L)
job.cancel()
println("Job 已取消:${job.isCancelled}")
Job 支持层次结构:如果父协程被取消,所有子协程会自动取消。SupervisorJob 改变了这种行为——子协程不会因为其中一个出错而被取消。
结构化并发意味着每个协程在特定的 CoroutineScope 中启动,并且 scope 在所有子协程完成工作之前不会结束。launch 完全支持这一原则——在另一个协程内部启动的协程是其子协程。
在嵌套启动 launch 时,会形成一棵协程树。父协程等待所有子协程完成,这保证了可预测的执行顺序并简化了资源管理。
fun main() = runBlocking {
launch {
launch {
delay(1000L)
println("子项 1 已完成")
}
launch {
delay(500L)
println("子项 2 已完成")
}
println("所有子项在此行之前已完成")
}
}
这种行为不同于基于线程的并发,在基于线程的并发中,子线程不绑定到父线程。在协程中,父协程不会完成,直到所有子协程完成它们的工作。
launch 中的异常处理取决于 Job 的类型。对于普通 Job,异常会传播到父协程并取消它。对于 SupervisorJob 或 SupervisorScope,异常被隔离——一个子协程的错误不会影响其他子协程。
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch {
try {
riskyOperation()
} catch (e: Exception) {
println("已捕获:${e.message}")
}
}
scope.launch {
println("这仍然可以运行,多亏了 SupervisorJob")
}
对于全局未捕获异常的处理,使用 CoroutineExceptionHandler。它捕获在协程内部未处理的异常,并允许记录错误而不会导致应用程序崩溃。
在 Android 中,launch 无处不在:从 ViewModel 到 WorkManager。主要模式是在 Fragment 中使用 lifecycleScope 和在 ViewModel 中使用 viewModelScope,以便在组件生命周期结束时自动取消协程。
class ProfileViewModel : ViewModel() {
private val repository = UserRepository()
fun loadProfile(userId: String) {
viewModelScope.launch(Dispatchers.IO) {
val profile = repository.fetchProfile(userId)
withContext(Dispatchers.Main) {
_profileState.update { it.copy(profile = profile) }
}
}
}
}
viewModelScope 在 ViewModel 销毁时自动取消协程。这消除了内存泄漏,并确保后台操作在上下文丢失后不会继续。此外,可以使用 withContext 在调度器之间切换。
常见问题
launch 返回 Job 并且不返回执行结果,而 async 返回 Deferred<T> 以获取结果。launch 用于 fire-and-forget 操作,async 用于需要等待并使用返回值的情况。
在保存的 Job 对象上调用 job.cancel()。对于分组取消,通过 scope.cancel() 取消整个 CoroutineScope。协程必须是协作式的——检查 isActive 或使用可检查的 suspend 函数。
在 launch 块内使用 try-catch,或在 CoroutineContext 中传递 CoroutineExceptionHandler。对于错误隔离,使用 SupervisorJob——这样,一个子协程中的异常不会取消其他子协程。
不能,launch 是 CoroutineScope 的扩展函数。没有 scope,协程无法遵循结构化并发原则。谨慎使用 GlobalScope.launch——这会创建一个不绑定到生命周期的协程。
数量受调度器的线程池限制。Dispatchers.Default 使用的线程数等于 CPU 核心数。Dispatchers.IO 支持最多 64 个线程。当启动更多协程时,它们会被放入队列。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。