Coroutines(协程)是Kotlin中用于异步编程的轻量级线程,可通过kotlinx.coroutines库获得。根据JetBrains Kotlin Documentation, 2026,Coroutines允许在不阻塞线程的情况下暂停函数的执行,这与传统的Thread不同。协程在有限的线程池上运行,使其比原生线程轻量数千倍。Kotlin Coroutines与Android Jetpack、Retrofit、Room以及Android生态系统的其他流行库完全集成。
要点
Coroutines(协程)是Kotlin中的异步编程机制,在kotlinx.coroutines库中实现。与操作系统线程不同,协程不绑定到特定线程:它们可以在一个线程上暂停并在另一个线程上恢复。一个线程可以执行数千个协程,在它们之间以最小的开销进行切换。
协程在Kotlin 1.3(2018)中作为实验性功能出现,并在Kotlin 1.5(2021)中稳定。Coroutines与async/await类似地解决了回调地狱问题,但提供了更丰富的API:通道(Channel)、Flow、Job层次结构中的异常处理以及直接与Android Lifecycle的集成。
根据JetBrains(2025)的数据,每个协程大约消耗100字节的内存,而原生线程则需要1+ MB。这使得可以在一个应用中运行数百万个协程而不会出现OutOfMemoryError的风险。正是协程的轻量性使其成为Android中异步编程的首选工具。
每个Kotlin协程通过Continuation Passing Style(CPS)编译为状态机。编译器为每个suspend函数添加一个隐藏的Continuation参数。Continuation包含恢复点和所有局部变量。当协程暂停时,运行时保存Continuation,当恢复时,在Dispatcher池中的任何可用线程上恢复它。
suspend — Kotlin的关键字,将函数标记为可暂停。这样的函数只能从另一个suspend函数或协程中调用。在suspend函数内部,可以以任意顺序调用其他suspend函数,每个调用点都是潜在的暂停点。
机制很简单:当一个suspend函数调用另一个suspend函数时,它会在该点暂停,释放线程。被调用的函数完成后,运行时从保存的位置继续执行。这称为cooperative cancellation — 没有任何线程被阻塞。
重要:suspend函数默认不是异步的。如果不使用launch或async,执行顺序保持顺序。suspend仅允许函数在不阻塞线程的情况下被暂停,并成为协程上下文的一部分。Continuation Passing Style是一种编译模型,其中每个suspend函数接收一个隐藏的Continuation回调,编译器生成一个状态机来管理暂停和恢复。
CoroutineScope是定义协程生命周期的上下文。所有协程必须在scope内部启动。当scope被取消时(例如,Activity结束时),其所有子协程会自动取消。这可以防止后台任务泄漏。Android Jetpack为每个组件提供现成的scope:viewModelScope用于ViewModel,lifecycleScope用于Activity和Fragment,它们在相应组件销毁时自动取消。
结构化并发 — 保证协程在其所有子协程完成之前不会完成的原则。Job层次结构形成一棵树:根协程创建parent job,子协程创建child jobs。Parent job的取消会传播到所有子项。结构化并发是协程与线程之间的根本区别。
| Scope | 使用位置 | 取消方式 |
|---|---|---|
| GlobalScope | 仅用于守护任务 | 不会自动取消 |
| viewModelScope | Android ViewModel | ViewModel清理时 |
| lifecycleScope | Android Activity/Fragment | Lifecycle销毁时 |
| coroutineScope | 在suspend函数内部 | 父任务取消时 |
普通的Job在某个子协程出错时会取消所有兄弟协程。SupervisorJob是一个例外:一个子协程的失败不会影响其他子协程。当多个独立任务并行执行且其中一个可能失败而不需要取消其他任务时,这很重要。
Dispatchers决定协程在哪些线程上执行。Dispatchers.Main — Android的主UI线程。Dispatchers.IO — 用于阻塞操作(网络、磁盘)的池。Dispatchers.Default — 用于CPU密集型任务。Dispatchers.Unconfined — 在当前线程上启动,但不保证停留在该线程上。选择正确的Dispatcher对性能至关重要:Default上的IO任务会阻塞计算池,而IO上的CPU任务会创建不必要的线程。
withContext — 用于在协程内部切换Dispatcher的函数。例如,执行JSON解析的suspend函数可以在计算期间切换到Dispatchers.Default,并返回到Dispatchers.Main以更新UI。withContext是Android开发中最常用的构建器。
launch — 启动协程,返回Job,不返回结果(fire-and-forget)。async — 启动协程,返回Deferred,可以通过await从中获取结果。runBlocking — 阻塞当前线程以执行协程(仅用于测试和main函数)。构建器的选择取决于场景:launch适用于事件和更新,async适用于有结果的任务,runBlocking仅适用于测试或入口点。
让我们来看三个实际场景:使用launch的基本协程、使用async的并行调用以及使用SupervisorJob的错误处理。
viewModelScope.launch在ViewModel上下文中启动协程。当ViewModel清理时,协程会自动取消。
class ProfileViewModel : ViewModel() {
fun loadUser() {
viewModelScope.launch(Dispatchers.IO) {
val user = api.fetchUser()
withContext(Dispatchers.Main) {
showUser(user)
}
}
}
}
coroutineScope与async并行启动三个请求。结果通过.await()收集。如果任何一个请求失败,所有请求都会被取消。
suspend fun loadDashboard(): Dashboard = coroutineScope {
val user = async { api.fetchUser() }
val posts = async { api.fetchPosts() }
val stats = async { api.fetchStats() }
Dashboard(user.await(), posts.await(), stats.await())
}
SupervisorJob允许每个协程独立完成。一个请求中的错误不会取消其他请求。
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch {
try { api.fetchUsers() } catch (e: Exception) { log(e) }
}
scope.launch {
try { api.fetchPosts() } catch (e: Exception) { log(e) }
}
线程(Thread)是操作系统的原语。每个线程都有自己的栈(约1 MB),并且需要系统调用来创建和切换。协程是语言原语,不绑定到操作系统。它们使用Continuation来保存状态,并在运行时级别切换而无需系统调用。
根据Google(2025)的数据,使用协程代替线程可将Android应用中后台任务的内存消耗降低90–95%。所有现代Android库(Retrofit、Room、WorkManager)都通过suspend函数内置了对协程的支持。Ktor(JetBrains的HTTP客户端框架)也完全构建在协程之上,为每个请求提供suspend函数,无需回调API。Room通过DAO中的suspend函数支持协程,允许在不阻塞主线程的情况下执行数据库查询。
线程仍然需要通过JNI的原生代码、无时间限制的阻塞CPU密集型调用(视频渲染、模拟)以及与C库集成时使用。对于其他所有情况 — 使用协程。
常见问题
协程 — 一种可暂停的工作单元,在现有线程上执行。线程是具有自己栈的系统资源。协程比线程轻量数千倍,并且在暂停时不会阻塞资源。
Dispatchers.IO用于阻塞I/O操作(网络、文件),可以在必要时创建新线程。Dispatchers.Default具有固定大小的池(CPU核心数),用于CPU密集型计算。
Job.cancel()取消协程及其所有子项。要检查协程内部的取消状态,请使用ensureActive() — 如果协程已取消,它会抛出CancellationException。
可以 — 通过kotlinx-coroutines-rx3库。它提供awaitSingle、awaitFirst等函数,用于将Observable/Single转换为suspend函数,并通过flowable进行反向转换。
Flow — 一种冷异步数据流,相当于协程的RxJava Observable。Flow按顺序发出值,并以异常或成功结束。支持map、filter、catch等操作符。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。