首页 / Android 入门教程 / 其他异步方案

Android 入门教程

其他异步方案

本教程共 100 篇 · 第 76 篇 · 更新于 2026-07-28 · 约 8 分钟阅读

AndroidAndroid 入门教程HandlerLooper线程池RxJavaAsyncTask

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.IODispatchers.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。
Note

RxJava 不是「已废弃」,是「不再首选」。复杂事件流(如搜索框防抖、多源合并)用 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,迁移策略:

  1. AsyncTask:直接换协程。doInBackgroundsuspendonPostExecutelaunch 里。
  2. Thread:换协程 launch(Dispatchers.IO)
  3. RxJava:简单的换协程 + Flow,复杂的复杂流暂时保留。
  4. Handler:能跑就保留,新代码用协程。

迁移时注意:

  • 异常处理:RxJava 的 onError 对应协程 try/catch 或者 CoroutineExceptionHandler
  • 取消:RxJava 的 Disposable.dispose() 对应协程 Job.cancel()
  • 调度器:Schedulers.io()Dispatchers.IOAndroidSchedulers.mainThread()Dispatchers.Main

常见坑

  1. 裸 Thread 持有 Activity:Activity 销毁后线程还在跑,内存泄漏。换协程用 lifecycleScope 自动取消。
  2. AsyncTask 不取消:Activity 销毁忘了 cancel,后台还在跑。换协程作用域自动取消。
  3. Handler 内存泄漏:内部类 Handler 持有 Activity,用静态内部类 + WeakReference 或者直接用协程。
  4. RxJava 忘记 dispose:订阅没管理,泄漏。协程的 scope 自动管理。

小结

Handler/Looper 是底层消息机制,Executor 是 Java 线程池,RxJava 是响应式编程库,AsyncTask 已废弃。新项目统一用协程 + Flow + WorkManager 这条线,老代码维护时按场景迁移。理解这些方案能让你看老代码不懵,但写新代码别再回去。

后台异步部分到这就讲完了。下一章我们进入架构与 Jetpack,先看官方的应用架构指南。