launch — 本质、fire-and-forget 协程启动及其工作原理

作者: IT Sectr 发布日期: 2026-06-22 阅读时间: 7 分钟

launch — Kotlin 中的 Coroutine Builder,它在不返回结果的情况下启动协程,返回一个 Job 对象来管理执行。该构建器实现了 fire-and-forget 模式:协程在后台线程中开始工作并自动结束。根据 JetBrains 文档,2024,launch 是在 Kotlin Coroutines 中启动并行任务的主要方式。

要点

  • launch — 用于启动协程而不返回结果的构建器(fire-and-forget)
  • Job — 协程管理对象,包含 cancel()、join()、isActive 方法
  • launch 与 CoroutineScope 集成,可自动取消子协程
  • CoroutineStart.LAZY 允许将启动延迟到显式调用 start() 或 join()
  • launch 在 Android 中用于数据库存储、网络调用和 UI 更新

Kotlin Coroutines 中的 launch 是什么?

launch — 是一个 Kotlin 扩展函数,通过 import kotlinx.coroutines 可用,它在指定的 CoroutineScope 中创建一个新的协程。与普通函数不同,launch 内的代码是并发执行的——协程可以在不阻塞线程的情况下挂起(suspend)并稍后恢复。

Launch 构建器是 Kotlin 协程模型的基本构建块。它不返回计算结果,但返回一个 Job 对象,允许控制协程的执行。通过 launch 在另一个协程内部启动的所有子协程都绑定到父协程。

launch 的语法和参数

Launch 构建器被定义为一个内联函数,带有多个参数:CoroutineScope、CoroutineContext、CoroutineStart 和 suspend 块。参数具有默认值,使得 launch 对典型场景很方便。

kotlin
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 — 返回的对象,代表协程的生命周期。Job 可以处于以下状态:New、Active、Completing、Completed、Cancelling、Cancelled。每个状态反映执行状态并允许对变化做出反应。

kotlin
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 改变了这种行为——子协程不会因为其中一个出错而被取消。

launch 中的结构化并发

结构化并发意味着每个协程在特定的 CoroutineScope 中启动,并且 scope 在所有子协程完成工作之前不会结束。launch 完全支持这一原则——在另一个协程内部启动的协程是其子协程。

嵌套的 launch 调用

在嵌套启动 launch 时,会形成一棵协程树。父协程等待所有子协程完成,这保证了可预测的执行顺序并简化了资源管理。

kotlin
fun main() = runBlocking {
    launch {
        launch {
            delay(1000L)
            println("子项 1 已完成")
        }
        launch {
            delay(500L)
            println("子项 2 已完成")
        }
        println("所有子项在此行之前已完成")
    }
}

这种行为不同于基于线程的并发,在基于线程的并发中,子线程不绑定到父线程。在协程中,父协程不会完成,直到所有子协程完成它们的工作。

launch 中的异常处理

launch 中的异常处理取决于 Job 的类型。对于普通 Job,异常会传播到父协程并取消它。对于 SupervisorJob 或 SupervisorScope,异常被隔离——一个子协程的错误不会影响其他子协程。

kotlin
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch {
    try {
        riskyOperation()
    } catch (e: Exception) {
        println("已捕获:${e.message}")
    }
}
scope.launch {
    println("这仍然可以运行,多亏了 SupervisorJob")
}

对于全局未捕获异常的处理,使用 CoroutineExceptionHandler。它捕获在协程内部未处理的异常,并允许记录错误而不会导致应用程序崩溃。

launch 在 Android 中的使用示例

在 Android 中,launch 无处不在:从 ViewModel 到 WorkManager。主要模式是在 Fragment 中使用 lifecycleScope 和在 ViewModel 中使用 viewModelScope,以便在组件生命周期结束时自动取消协程。

kotlin
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 在调度器之间切换。

常见问题

Kotlin Coroutines 中 launch 和 async 有什么区别?

launch 返回 Job 并且不返回执行结果,而 async 返回 Deferred<T> 以获取结果。launch 用于 fire-and-forget 操作,async 用于需要等待并使用返回值的情况。

如何取消通过 launch 启动的协程?

在保存的 Job 对象上调用 job.cancel()。对于分组取消,通过 scope.cancel() 取消整个 CoroutineScope。协程必须是协作式的——检查 isActive 或使用可检查的 suspend 函数。

如果 launch 抛出异常该怎么办?

在 launch 块内使用 try-catch,或在 CoroutineContext 中传递 CoroutineExceptionHandler。对于错误隔离,使用 SupervisorJob——这样,一个子协程中的异常不会取消其他子协程。

能否在没有 CoroutineScope 的情况下启动 launch?

不能,launch 是 CoroutineScope 的扩展函数。没有 scope,协程无法遵循结构化并发原则。谨慎使用 GlobalScope.launch——这会创建一个不绑定到生命周期的协程。

可以同时启动多少个 launch 协程?

数量受调度器的线程池限制。Dispatchers.Default 使用的线程数等于 CPU 核心数。Dispatchers.IO 支持最多 64 个线程。当启动更多协程时,它们会被放入队列。

总结

  • launch — 用于 fire-and-forget 协程的主要构建器,返回 Job 对象
  • Job 提供管理方法:cancel()、join()、isActive、isCancelled、isCompleted
  • 结构化并发 保证父协程取消时自动取消子协程
  • 异常处理 取决于 Job 类型:普通 Job 传播错误,SupervisorJob 隔离错误
  • lifecycleScopeviewModelScope — 在 Android 中启动 launch 的现成解决方案
  • CoroutineStart.LAZY 将启动延迟到显式调用 start() 或 join()
  • launch 在 Android 中用于网络请求、数据库写入、分析和缓存

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读