从 Callback 到 Coroutines:Android 异步并发方案的演进

前言:同时发起 A、B、C 三个网络请求,全部完成后再串行请求 D ------ 这是移动端最常见的并发场景之一。本文通过完整可运行的代码,横向对比 5 种主流实现方案,带你看清各自的本质与取舍。


📌 场景定义

scss 复制代码
[请求 A] ─┐
[请求 B] ─┼─ (并行) ──▶ 全部完成 ──▶ [请求 D] ──▶ 拿到最终结果
[请求 C] ─┘

这个"3并1串"的模式在真实业务中极为常见,比如:首页同时拉取轮播图、文章列表、置顶文章,全部就绪后再根据结果请求个性化推荐。


方案一:Java Callbacks(嵌套回调)

实现代码

⚠️ 核心问题

注意 :这段代码虽然嵌套了 4 层,但 A、B、C 三个请求实际上是串行 的,并非真正的并行,要在纯 Callback 模式下实现并行,你还需要额外维护一个计数器或 AtomicInteger,代码复杂度会进一步爆炸。

维度 评估
并行实现 ❌ 需要手动计数器或者JUC,极易出错
代码可读性 ❌ 向右无限延伸,逻辑碎片化
错误处理 ❌ 每层都要重复 onFailure
适用场景 仅适合单次简单异步

方案二:CountDownLatch(显式锁阻塞)

实现代码

⚠️ 核心问题

latch.await() 会让后台线程进入 Blocked(阻塞) 状态,线程被占用但什么工作也不做。在主线程等待,这会导致ANR。

更危险的是:任何一个分支如果忘记调用 countDown()(例如某个异常路径没覆盖到),整个流程将永久死等

维度 评估
并行实现 ✅ 真正并行
线程状态 ❌ 阻塞(Blocked),浪费资源
健壮性 ❌ 遗漏 countDown = 死锁
代码复杂度 中等

方案三:CompletableFuture(链式异步)

实现代码

⚠️ 核心问题

逻辑链路清晰了许多,但有两个隐患:

  1. 调试地狱:异常堆栈在异步链条中会丢失调用上下文,出了问题很难定位到具体是哪一步。
  2. 心智负担thenApply / thenCompose / thenCombine / allOf ------ 你需要熟练掌握这些操作符的区别才能正确使用。
维度 评估
并行实现 ✅ 真正并行
线程状态 ✅ 非阻塞(线程池驱动)
调试体验 ❌ 异步链中堆栈丢失
API 学习成本 中等偏高

方案四:RxJava3(响应式流)

实现代码

⚠️ 核心问题

RxJava 非常强大,但在这个场景下有"大炮打蚊子"之嫌:

  1. 框架重量级:引入 RxJava3 + RxAndroid,仅为处理简单的顺序业务逻辑,成本偏高。
  2. 生命周期管理subscribe 返回的 Disposable 必须在 onDestroy 中手动 dispose(),否则会内存泄漏。
kotlin 复制代码
// 不处理 Disposable = 内存泄漏!
val disposable = observable.subscribe(...)
// 必须在 onDestroy 中:
disposable.dispose()
维度 评估
并行实现 ✅ 真正并行(zip 操作符)
表达力 ✅ 复杂数据流处理极强
框架重量 ❌ 引入成本高
生命周期 ❌ 需手动管理 Disposable

方案五:Kotlin Coroutines(协程 ⭐ 推荐)

实现代码

✅ 为什么协程是最佳答案

1. 挂起 ≠ 阻塞

await() 期间,线程不会被占用------协程被"挂起",线程可以去做其他事情。这与 CountDownLatch.await() 的死等有本质区别。

ini 复制代码
CountDownLatch:  线程 ──[Blocked 死等]──────────────▶ 继续
Coroutine:       线程 ──[释放去处理其他协程]──▶ 恢复继续

2. 结构化并发,生命周期自动管理

lifecycleScope.launch 会自动将协程的生命周期绑定到 Activity,当 Activity 销毁时,所有未完成的请求会自动取消,无需任何手动清理。

3. 用线性代码表达异步逻辑

上面的协程代码读起来就像同步代码,但底层完全是异步非阻塞的。这就是协程最核心的价值:用人类最自然的线性思维,编写高性能的异步逻辑

维度 评估
并行实现 ✅ async/await
线程状态 ✅ 挂起(Suspended),不占资源
生命周期 ✅ 自动绑定,无需手动取消
代码可读性 ✅ 伪同步,极易维护
学习成本 低(语言原生支持)

📊 五方案横向对比

方案 真正并行 等待机制 线程状态 生命周期管理 代码复杂度 可维护性
Callbacks 嵌套触发 运行/空闲 手动 极高
CountDownLatch 显式锁阻塞 Blocked 手动 ⭐⭐
CompletableFuture 回调链驱动 运行(线程池) 手动 中高 ⭐⭐⭐
RxJava3 操作符聚合 运行(线程池) 手动 Disposable ⭐⭐⭐
Coroutines 非阻塞挂起 Suspended 自动 极低 ⭐⭐⭐⭐⭐

💡 总结

每一种方案的演进,都是在解决上一代的痛点:

  • Callbacks ------ 最原始,让代码横向无限延伸,并行更是一场噩梦。
  • CountDownLatch ------ 解决了并行,但用阻塞线程换来的,有ANR和死锁风险。
  • CompletableFuture ------ 链式调用更优雅,但调试困难,API 理解成本不低。
  • RxJava3 ------ 对复杂数据流极其强大,但在简单场景下是过度设计,生命周期还要自己管。
  • Kotlin Coroutines ------ 消灭了锁,消灭了回调,让代码回归线性,阅读起来顺畅。

GitHub

相关推荐
鹏程十八少2 小时前
4. 2026金三银四 Android OkHttp 面试核心 45 问:从源码到架构深度解析
android·前端·面试
90后的晨仔11 小时前
Android Studio 项目模板完全指南
android
summerkissyou198711 小时前
Android-SurfaceView-投屏-常见问题
android·surfaceview
明天就是Friday11 小时前
Android实战项目④ OkHttp WebSocket开发即时通讯App 完整源码详解
android·websocket·okhttp
吉哥机顶盒刷机12 小时前
好物分享:DNA-Android-4.0.5安卓固件解包、打包工具
android·好物分享
三棱球12 小时前
App逆向学习笔记(三)——Android开发入门课
android·笔记
安卓机器13 小时前
rom定制系列------魅族16x 解锁bl root与Flyme9安卓10线刷固件 传感器修复
android·魅族16x玩机
wellc15 小时前
MySQL Workbench菜单汉化为中文
android·数据库·mysql