《Android Handler:线程间通信的核心实现》

在 Android 中,Handler 机制是一套用于线程间通信的核心机制,主要解决 "子线程无法直接更新 UI" 的问题(Android 规定 UI 操作必须在主线程执行,否则会抛出异常),同时也用于实现延迟任务、定时任务等场景。

1. Message(消息)

  • 是线程间传递的数据载体,可携带简单数据(如what、arg1、arg2)或复杂对象(obj)。
  • 为了避免频繁创建对象,可通过Message.obtain()从消息池获取(复用机制),比new Message()更高效。

2. MessageQueue(消息队列)

  • 是一个单链表结构 的队列,用于存储Handler发送的Message,按消息的触发时间(when)排序(早触发的在前)。
  • 每个线程最多只有一个MessageQueue,由Looper创建并管理。

3. Looper(消息循环器)

  • 是 "消息循环" 的核心,负责不断从 MessageQueue 中取出消息 ,并分发到对应的Handler处理。
  • 每个线程最多只有一个Looper(通过ThreadLocal实现线程隔离,确保线程私有)。
  • 主线程(UI 线程)在启动时会自动初始化Looper(通过ActivityThread.main()方法),因此主线程可直接使用Handler;子线程若要使用Handler,需手动调用Looper.prepare()初始化Looper,再调用Looper.loop()启动循环。

4. Handler(处理器)

  • 负责发送消息到 MessageQueue (如sendMessage()、post()等方法),并处理 Looper 分发的消息 (重写handleMessage()方法)。
  • 一个Handler关联一个Looper(创建时默认绑定当前线程的Looper),因此可通过Handler将消息从一个线程发送到另一个线程的MessageQueue。

工作流程

  1. 初始化 Looper :线程启动时,若需使用 Handler 机制,需通过Looper.prepare()创建Looper和对应的MessageQueue(主线程默认初始化)。
  2. 创建 Handler :在目标线程(如主线程)创建Handler,它会自动绑定当前线程的Looper和MessageQueue。
  3. 发送消息 :其他线程(如子线程)通过Handler的sendMessage()(发送Message)或post()(发送Runnable,内部会包装为Message)将消息加入MessageQueue。
  4. 消息循环 :Looper.loop()启动死循环,不断从MessageQueue中取出消息(若队列空则阻塞,释放 CPU),并通过Message中关联的Handler分发消息。
  5. 处理消息 :Handler收到消息后,通过dispatchMessage()方法处理(优先执行Message的Runnable,其次是Handler的handleMessage())。

关键细节

  • 线程与 Looper 的关系:1 个线程 → 1 个 Looper → 1 个 MessageQueue → N 个 Handler(多个 Handler 可绑定同一 Looper)。
  • Looper 的死循环为何不 ANR :当MessageQueue为空时,Looper会阻塞在next()方法(通过 Linux 的epoll机制休眠),不占用 CPU;有新消息时会被唤醒,因此不会导致 ANR。
  • 内存泄漏风险 :若Handler是 Activity 的非静态内部类,会持有 Activity 的强引用;若消息在队列中延迟未处理,会导致 Activity 无法被回收(内存泄漏)。解决:将Handler定义为静态内部类 ,并通过弱引用(WeakReference) 持有 Activity;在 Activity 销毁时(onDestroy())调用handler.removeCallbacksAndMessages(null)移除所有消息。

典型场景

  • 子线程执行耗时操作(如下载、数据库查询)后,通过Handler通知主线程更新 UI。
  • 实现延迟任务(如handler.postDelayed(runnable, 1000))。
  • 主线程向子线程发送指令(如停止子线程的任务)。

1.子线程→主线程通信

kotlin 复制代码
class MainActivity : AppCompatActivity() {
    companion object {
        private const val TAG = "HandlerDemo"
        private const val WHAT_UPDATE = 2
    }


    // 后台线程与其 Handler(示例使用 HandlerThread)
    private lateinit var workerThread: HandlerThread
    private lateinit var workerHandler: Handler

    // 界面元素:用于展示处理结果
    private lateinit var statusText: TextView
    private lateinit var actionButton: Button

    // Message 处理示例:在主线程中统一处理结构化消息
    private val messageHandler = object : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                WHAT_UPDATE -> {
                    val text = "Updated: ${msg.obj}"
                    Log.d(TAG, "handleMessage WHAT_UPDATE: ${msg.obj}")
                    // 更新到界面
                    if (::statusText.isInitialized) {
                        statusText.text = text
                    }
                }
                else -> {
                    Log.d(TAG, "handleMessage what=${msg.what}")
                }
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        // 绑定 TextView,用于展示主线程/后台线程的处理结果
        statusText = findViewById(R.id.statusText)
        actionButton = findViewById(R.id.actionButton)

        // 2) HandlerThread:创建后台线程并投递任务
        workerThread = HandlerThread("DemoWorker")
        workerThread.start()
        workerHandler = Handler(workerThread.looper)

        // 点击按钮后触发:仅演示子线程 -> 主线程通信
        actionButton.setOnClickListener {
            statusText.text = "Starting..."

            // 后台线程:执行任务并通过 Message 回到主线程
            workerHandler.post {
                Log.d(TAG, "workerHandler.post on ${Thread.currentThread().name}")
                Thread.sleep(300)
                val msg = messageHandler.obtainMessage(WHAT_UPDATE, "Result from worker")
                messageHandler.sendMessage(msg)
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        // 清理消息与回调,避免泄漏;安全退出后台线程
        messageHandler.removeCallbacksAndMessages(null)
        if (::workerThread.isInitialized) {
            workerThread.quitSafely()
        }
    }
}

2.实现延迟任务

kotlin 复制代码
// 后台线程:延迟启动任务,然后立即把结果发回主线程
Log.d(TAG, "schedule workerHandler.postDelayed(+5s)")
workerHandler.postDelayed({
    Log.d(TAG, "workerHandler.postDelayed on ${Thread.currentThread().name}")
    Thread.sleep(300)
    val msg = messageHandler.obtainMessage(WHAT_UPDATE, "Result from worker (delayed start)")
    messageHandler.sendMessage(msg)
}, 5000L)
相关推荐
biu_biubiu21 分钟前
我开源了 Codebook
前端·vue.js
下山22 分钟前
怎么把带自研插件的 DeepSeek Harness 部署到云服务器
前端
彭亚川Allen29 分钟前
985 在职研究生到底怎么样?是不是水硕?是不是.....?
前端·后端·程序员
YufJi32 分钟前
两棵树,两个线程:一个小程序引擎的架构拆解
前端
super_Omni41 分钟前
Chrome 插件入驻实战:从 Manifest V3 到过审上架的完整路径
大数据·前端·chrome
腾讯数据架构师1 小时前
任务跑完怎么直接出图表?CubeStudio 任务可视化 ECharts 数据格式实操(CSV/JSON + metric.json,11 种图表一键渲染)
前端·json·echarts·数据可视化
小小小米粒1 小时前
不是在构造方法「之前」修饰 Bean、注入;是构造方法跑完之后,才做属性注入,再跑 @PostConstruct。
前端
小李科技1 小时前
【性能优化】事件委托:目标定位与 React/Vue 实战
前端·vue.js·react.js·面试·性能优化·前端框架
QianhangQianping2 小时前
一块大屏的数据在浏览器里怎么变成画面:背景、子屏、前景三层结构
前端·低代码·架构·编辑器·数据可视化·大屏可视化
梦想很大很大2 小时前
让 Codex 自由发挥,给 nvm-desktop 换个 UI
前端·人工智能·设计