其他异步方案
本教程共 100 篇 · 第 76 篇 · 更新于 2026-07-28 · 约 8 分钟阅读
76. 其他异步方案
本节目标:认识 Android 历史上出现过的其他异步方案,包括 Handler/Looper、线程池、RxJava、AsyncTask,理解它们的定位和现状,知道什么场景还能用、什么时候该用协程替代。
协程是现代 Android 异步的首选,但老代码、第三方库里还会遇到其他方案。这章把剩下的补齐,不展开,让你看老代码不懵。
Handler 和 Looper
Handler 和 Looper 是 Android 最底层的消息机制。所有「主线程更新 UI」的方案,归根到底都用它。
打个比方,主线程是个工作人员,手里有个待办清单(MessageQueue)。子线程像其他同事,把任务(Message)扔到清单上,主线程一条条处理。Handler 就是扔任务的快递员,Looper 是循环处理清单的引擎。
// 子线程发消息到主线程
val handler = Handler(Looper.getMainLooper())
handler.post {
textView.text = "更新了"
}
// 延迟 1 秒
handler.postDelayed({
textView.text = "1 秒后更新"
}, 1000)
几个要点:
- 每个
Looper管一个MessageQueue,主线程默认有MainLooper。 Handler绑定到某个Looper,往那个队列里扔消息。HandlerThread是带 Looper 的子线程,需要顺序处理后台任务时用。
Note现在新代码里基本不直接写 Handler。但理解它对调试有用—ANR、卡顿分析常涉及主线程的消息队列。
线程池:Executor
Java 提供的线程池,比裸 Thread 高效。Android 里直接能用。
import java.util.concurrent.Executors
// 固定大小线程池
val executor = Executors.newFixedThreadPool(4)
executor.execute {
// 子线程任务
val result = doHeavyWork()
// 回主线程
runOnUiThread {
textView.text = result
}
}
几种常用线程池:
newFixedThreadPool(n):固定 n 个线程。newCachedThreadPool():按需创建,空闲 60 秒回收。newSingleThreadExecutor():单线程,任务排队执行。newScheduledThreadPool(n):支持定时和周期任务。
协程底层就是用线程池实现的(Dispatchers.IO、Dispatchers.Default),但 API 更友好。
Tip老代码里看到
Executor,别急着重写。能用就保留,新功能用协程就行。两者可以共存。
RxJava
RxJava 是响应式编程库,曾经是 Android 异步的事实标准,直到协程出现。
api.getData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map { it.name }
.filter { it.isNotEmpty() }
.subscribe(
{ name -> textView.text = name },
{ e -> Log.e("App", "error", e) }
)
特点:
- 强大的操作符链式调用。
- 处理复杂数据流和事件流很优雅。
- 学习曲线陡,过度使用容易写出天书。
- 异常处理和取消机制完善。
和协程的关系:
- 协程能覆盖 RxJava 90% 的场景。
- 剩下的复杂流处理 RxJava 还有优势。
- 新项目首选协程,老项目维护用 RxJava。
- Jetpack 库(Room、WorkManager)同时支持 Flow 和 RxJava。
NoteRxJava 不是「已废弃」,是「不再首选」。复杂事件流(如搜索框防抖、多源合并)用 RxJava 仍然清晰。简单异步用协程更顺手。
AsyncTask:已废弃
AsyncTask 是早期的异步工具,Android 11(API 30)正式废弃。
它的写法:
class MyTask : AsyncTask<Void, Int, String>() {
override fun doInBackground(vararg params: Void): String {
// 后台线程
return "result"
}
override fun onPostExecute(result: String) {
// 主线程
textView.text = result
}
override fun onProgressUpdate(vararg values: Int?) {
// 主线程,更新进度
}
}
MyTask().execute()
为什么废弃:
- 容易内存泄漏(持有 Activity 引用)。
- 配置变化(旋转屏幕)时行为诡异。
- 异常处理弱,崩溃难排查。
- 串行执行(早期版本),并行有坑。
Warning
AsyncTask已废弃,新代码别用。老代码迁移到协程是标准做法:AsyncTask.doInBackground的逻辑移到suspend函数,onPostExecute移到launch块里。
Thread 裸线程
最原始的方式:
Thread {
val result = doWork()
runOnUiThread {
textView.text = result
}
}.start()
问题:
- 创建和销毁开销大。
- 没有线程池,难管理。
- 取消困难,要手动加 flag。
- 异常处理弱。
除了极少数场景(如启动一个长期运行的后台监听线程),都该用协程或线程池替代。
CountDownTimer
倒计时场景的便捷工具:
object : CountDownTimer(30000, 1000) {
override fun onTick(millisUntilFinished: Long) {
textView.text = "剩余 ${millisUntilFinished / 1000} 秒"
}
override fun onFinish() {
textView.text = "完成"
}
}.start()
底层也是 Handler。简单倒计时够用,复杂场景用协程的 delay 更灵活:
suspend fun countdown(seconds: Int) {
for (i in seconds downTo 1) {
println("剩余 $i 秒")
delay(1000)
}
println("完成")
}
LiveData + 协程
Jetpack 提供的 liveData 构建器,用协程产出 LiveData:
val user: LiveData<User> = liveData {
val data = repo.fetchUser()
emit(data)
}
新项目更推荐直接用 Flow + collectAsState,LiveData 主要用在 View 体系或者和老代码兼容。
该选什么
按场景选型:
| 场景 | 推荐 |
|---|---|
| 普通异步任务 | 协程 |
| 持续数据流 | Flow |
| 状态管理 | StateFlow |
| 复杂事件流合并 | Flow 或者 RxJava |
| 定时任务(应用内) | 协程 delay |
| 后台可靠任务 | WorkManager |
| 老代码维护 | 看原方案 |
| 子线程更新 UI 一行 | runOnUiThread 或 Handler |
Tip新项目统一协程 + Flow + WorkManager 这条线,几乎覆盖所有场景。RxJava 在复杂流处理时再考虑。
老代码迁移建议
遇到老代码用 AsyncTask、Thread、RxJava,迁移策略:
- AsyncTask:直接换协程。
doInBackground→suspend,onPostExecute→launch里。 - Thread:换协程
launch(Dispatchers.IO)。 - RxJava:简单的换协程 + Flow,复杂的复杂流暂时保留。
- Handler:能跑就保留,新代码用协程。
迁移时注意:
- 异常处理:RxJava 的
onError对应协程try/catch或者CoroutineExceptionHandler。 - 取消:RxJava 的
Disposable.dispose()对应协程Job.cancel()。 - 调度器:
Schedulers.io()→Dispatchers.IO,AndroidSchedulers.mainThread()→Dispatchers.Main。
常见坑
- 裸 Thread 持有 Activity:Activity 销毁后线程还在跑,内存泄漏。换协程用
lifecycleScope自动取消。 - AsyncTask 不取消:Activity 销毁忘了 cancel,后台还在跑。换协程作用域自动取消。
- Handler 内存泄漏:内部类 Handler 持有 Activity,用静态内部类 + WeakReference 或者直接用协程。
- RxJava 忘记 dispose:订阅没管理,泄漏。协程的 scope 自动管理。
小结
Handler/Looper 是底层消息机制,Executor 是 Java 线程池,RxJava 是响应式编程库,AsyncTask 已废弃。新项目统一用协程 + Flow + WorkManager 这条线,老代码维护时按场景迁移。理解这些方案能让你看老代码不懵,但写新代码别再回去。
后台异步部分到这就讲完了。下一章我们进入架构与 Jetpack,先看官方的应用架构指南。