定义
协程(Coroutine)是Kotlin提供的一种轻量级线程,用于简化异步编程。它可以在单线程内实现并发操作,通过挂起(suspend) 和恢复(resume)机制,让异步代码看来像同步代码一样直观。
协程的核心是挂起函数(suspend),它可以在不阻塞当前线程的情况下等待操作完成,完成后自动恢复执行。
特点
-
轻量级
协程运行在用户态,不直接依赖操作系统线程,可以在一个线程中运行成千上万个协程,开销极小。
-
结构化并发
协程通过作用域(CoroutineScope)管理生命周期,确保所有字协程在父协程完成前完成,避免资源泄露。
-
取消机制
协程支持协作取消,可以随时取消不再需要的任务,释放资源。
-
异步处理简单
使用常规的
try-catch即可捕捉协程内部异常,或者通过CoroutineExceptionHandler统一处理。 -
与Android生命周期集成
官方提供了
lifecycleScope(Activity/Fragment)和viewModelScope(ViewModel),自动感知生命周期,在销毁时自动取消协程。 -
调度器(Dispatcher)
协程可以灵活切换线程:
Dispathchers.Main(主线程)、Dispatchers.IO(IO密集型任务)、Disapatchers.Default(CPU密集型任务)等。
Kotlin 协程作用域
Kotlin 协程主要有以下几种作用域构建器:
GlobalScope(全局作用域)
- 生命周期:应用程序整个生命周期
- 使用场景:不推荐使用,仅适用于与应用程序生命周期的相同任务
GlobalScope.launch {
// 不推荐
}
特点:
- 不需要在任何范围内,即可启动对象
- 容易导致内存泄漏
- 无法自动取消
- 以及被标记
@DelicateCoroutinesApi⚠️
lifecycleScope(生命周期作用域)
- 生命周期:与
Lifecycle绑定(Activity/Fragment) - 使用场景:UI相关的协程操作
lifecycleScope.launch {
// 推荐用于 Activity/Fragment
}
特点:
- 自动管理生命周期
- Lidecycle销毁时自动取消
- 需要引入
lifecycle-runtime-ktx库 - 推荐使用✅
viewModeScope(ViewModel 作用域)
- 生命周期:与
ViewModel绑定 - 使用场景:在ViewModel中执行后台任务
viewModelScope.launch {
// 推荐用于 ViewModel
}
特点:
- ViewModel清除时候自动取消
- 需要引入
lifecycle-viewmodel-ktx - 推荐使用✅
coroutineScope(协程作用域)
- 生命周期:等待所有的子协程完成
- 使用场景:需要等待所有的子任务完成的场景
coroutineScope {
launch { /* 任务 1 */ }
launch { /* 任务 2 */ }
// 等待所有任务完成
}
特点:
- 会阻塞当前协程直到所有的子协程完成场景
- 失败会传播异常,一个失败就会抛出异常一损俱损
- 结构化并发
SupervisorScop(监督作用域)
- 生命周期:等待所有的子协程完成
- 使用场景:子任务相互独立,一个失败不影响其他任务
supervisorScope {
launch {
// 失败不会影响其他协程
}
launch {
// 继续执行
}
}
特点:
- 子协程失败不会导致其他协程取消,独立失败,互不影响
- 需要手动处理异常
- 更灵活的错误处理
CoroutineScope(自定义作用域)
- 生命周期:手动控制
- 使用场景:自定义生命周期管理
class MyRepository {
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
fun doWork() {
scope.launch {
// 后台工作
}
}
fun cleanup() {
scope.cancel()
}
}
特点:
- 需要手动创建和取消
- 灵活性更高
- 需要谨慎管理避免内存泄漏
协程作用域对照表
| 作用域 | 生命周期 | 自动取消 | 异常传播 | 使用场景 | 推荐度 |
|---|---|---|---|---|---|
| GlobalScope | 应用全程 | ❌ | ❌ | 不推荐使用 | ⚠️ 不推荐 |
| lifecycleScope | Lifecycle | ✅ | ✅ | UI 层 (Activity/Fragment) | ⭐⭐⭐⭐⭐ |
| viewModelScope | ViewModel | ✅ | ✅ | ViewModel 层 | ⭐⭐⭐⭐⭐ |
| coroutineScope | 子协程完成 | ✅ | ✅ | 需要等待所有子任务 | ⭐⭐⭐⭐ |
| supervisorScope | 子协程完成 | ✅ | ❌ | 子任务相互独立,互不影响 | ⭐⭐⭐⭐ |
| CoroutineScope | 手动控制 | ❌ | 可配置 | 自定义场景 | ⭐⭐⭐ |
Kotlin 协程启动器:launch、async、runBlocking 详解
Launch
//源代码
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
特点
- 返回类型:Job 【不返回结果】
- 使用场景:“发射并忘记”,不需要返回结果的异步操作
- 异常处理:异常会被传播到
CoroutineExceptionHandler或者 父协程
参考案例:
// 在已有的协程作用域中
val job = coroutineScope.launch {
delay(1000)
println("执行后台任务")
}
// 可以取消任务,有内鬼终止交易
job.cancel()
async
//源代码
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T> {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyDeferredCoroutine(newContext, block) else
DeferredCoroutine<T>(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
特点
- 返回类型:
Deferred<T>可等待的泛型结果 - 使用场景:需要返回结果的异步操作,可并行执行多个任务
- 异常处理:异常会在调用
wawit()时候抛出
参考案例:
val scope = CoroutineScope(Dispatchers.IO + Job())
// 并行执行多个任务
val deferred1 = scope.async { fetchData1() }
val deferred2 = scope.async { fetchData2() }
// 等待结果
val result1 = deferred1.await()
val result2 = deferred2.await()
// 或使用 awaitAll 并行等待
val results = listOf(deferred1, deferred2).awaitAll()
runBlocking
//源代码
public actual fun <T> runBlocking(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
val currentThread = Thread.currentThread()
val contextInterceptor = context[ContinuationInterceptor]
val eventLoop: EventLoop?
val newContext: CoroutineContext
if (contextInterceptor == null) {
// create or use private event loop if no dispatcher is specified
eventLoop = ThreadLocalEventLoop.eventLoop
newContext = GlobalScope.newCoroutineContext(context + eventLoop)
} else {
// See if context's interceptor is an event loop that we shall use (to support TestContext)
// or take an existing thread-local event loop if present to avoid blocking it (but don't create one)
eventLoop = (contextInterceptor as? EventLoop)?.takeIf { it.shouldBeProcessedFromContext() }
?: ThreadLocalEventLoop.currentOrNull()
newContext = GlobalScope.newCoroutineContext(context)
}
val coroutine = BlockingCoroutine<T>(newContext, currentThread, eventLoop)
coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
return coroutine.joinBlocking()
}
特点
- 返回类型:
T阻塞当前线程直到完成 - 使用场景:桥接同步和异步代码,测试环境
- 异常处理:直接抛出异常
参考案例
// 阻塞主线程
fun main() {
runBlocking {
val result = async { loadData() }.await()
println(result)
}
// 上面的代码会阻塞直到协程完成
}
// 测试中使用
@Test
fun testCoroutine() = runBlocking {
val result = repository.getData()
assertEquals("expected", result)
}
对比总结表 launch、async、runBlocking
| 启动器 | launch | async | runBlocking |
|---|---|---|---|
| 返回值 | Job | Deferred<T> | T |
| 是否阻塞 | 否 | 否 | 是 |
| 获取结果 | ❌ | ✅ await() | ✅ 直接返回 |
| 使用场景 | 不关心结果 | 需要结果 | 测试/桥接 |
| UI 线程可用 | ✅ | ✅ | ❌ |
| 性能开销 | ⭐⭐⭐ | ⭐⭐ | ⭐ |
suspend 挂起状态
一个挂起任务runSuspendTalks
suspend fun runSuspendTalks(): String {
log("协程挂起任务 runSuspendTalks ")
delay(1000 * 3)
return "100"
}
反编译成Java语音查看区别
// 方法签名添加了 Continuation 参数
@Nullable
public final String runSuspendTalks(@NotNull Continuation<? super runSuspendTalks> continuation) {
// 检查 Continuation 是否是特定状态机的实例
if (continuation instanceof runSuspendTalks$CoroutineImpl) {
runSuspendTalks$CoroutineImpl coroutineImpl = (runSuspendTalks$CoroutineImpl)continuation;
int label = coroutineImpl.label;
coroutineImpl.label = 0;
// 根据状态执行不同的代码段
if (label == 0) {
// 正常执行逻辑
this.log("协程挂起任务 runSuspendTalks ");
// 调用 delay 时会挂起
Object result = DelayKt.delay(3000L, coroutineImpl);
if (result == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
return result; // 挂起,返回挂起点
}
// 恢复执行
return "100";
} else if (label == 1) {
// 从 delay 挂起点恢复
// ... 继续执行后续代码
return "100";
}
}
// 创建新的状态机实例
runSuspendTalks$CoroutineImpl impl = new runSuspendTalks$CoroutineImpl(this, continuation);
return impl.invokeSuspend(Unit.INSTANCE);
}
============================== 生成的状态机类(伪代码): ===========================================
// 编译器生成的状态机类
final class runSuspendTalks$CoroutineImpl extends CoroutineImpl {
int label;
Object L$0; // 局部变量存储
public final Object invokeSuspend(Object result) {
// 状态机核心逻辑
switch(label) {
case 0:
// 第一次执行
label = 1;
this.result = result;
break;
case 1:
// 从挂起恢复
break;
}
return result;
}
}
关键点
- Continuation参数:每个
suspend函数都会多出一个Continuation<T>参数,用于回调恢复 - 状态机:编译器将
suspend函数转换成有限状态机,通过label标记执行位置 - 挂起点:每次调用其他
suspend函数(如:delay)时:- 保存当前状态到
Continuation - 返回特殊值 COROUTINE_SUSPENDED
- 异步操作完成后,通过Continuation.resume()恢复
- 保存当前状态到
- 局部变量提示:suspend函数中的局部变量会提升状态机的字段
withContent调度器
可用withContent切换协程上下文的线程,如 I/O 操作、CPU 密集型任务等
suspend fun fetchUserData(): String {
// 在 IO 线程执行网络请求或数据库操作
val userData = withContext(Dispatchers.IO) {
// 执行耗时的 I/O 操作
performNetworkRequest()
}
// 自动返回到原始上下文(可能是主线程)
// 更新 UI
updateUI(userData)
return userData
}
-
Dispatchers.Main:Android 主线程,用于更新 UI -
Dispatchers.IO:适用于 I/O 密集型任务,如网络请求、文件读写 -
Dispatchers.Default:适用于 CPU 密集型任务,例如算法,非I/O操作流 -
Dispatchers.Unconfined:无限制调度器,很少使用
cancel 取消协程
- Job.cancel()
val job = launch {
// 协程任务
}
job.cancel() // 取消协程
- async 返回的是 Deferred<T>(继承自 Job),取消方式类似 deferred.cancel()
val deferred = scope.async {}
// 取消 async 协程
deferred.cancel()
- 取消整个 CoroutineScope,及父作用域
val scope = CoroutineScope(Dispatchers.IO + Job())
scope.launch { /* ... */ }
scope.coroutineContext[Job]?.cancel() // 取消作用域内所有协程