Android Handler

面试可先答:四件套 Message、MessageQueue、Looper、Handler;线程切换本质是多线程共享同一 MessageQueue,通过 Handler 投递、Looper 分发;子线程需 Looper.prepare() + loop();注意内存泄漏与 removeCallbacksAndMessages。详见笔记《Android 面试·分章系统复习》第七章 Handler 机制。

阅读建议:先看 应用场景四件套,再 线程切换本质ThreadLocal同步屏障,最后 HandlerThread 案例常见问题


应用场景

Android 更新 UI 必须在主线程执行。Handler 用于在不同线程之间传递消息,把「更新 UI」等任务投递到主线程执行。


核心模块与架构(四件套)

组件作用
Message消息载体,含 whatobjtarget(归属 Handler)、when(执行时间)
MessageQueue时间顺序排列的优先级单链表
Looper线程内的死循环,从 MessageQueue 取消息并分发
Handler发送消息入队,并接收 Looper 回传的消息在对应线程处理

架构示意:

12


线程切换的本质:数据如何跨越线程?

结论:多线程共享同一个 MessageQueue 对象,通过 Handler 引用实现跨线程投递。

  1. 绑定引用:Handler 创建时绑定一个 Looper(及该 Looper 唯一的 MessageQueue)。
  2. 跨线程入队:任意线程调用 handler.sendMessage(),底层都是操作该 Handler 持有的 mQueue
  3. 同步MessageQueue.enqueueMessage 内用 synchronized(this) 保证线程安全。
  4. 唤醒执行:消息入队后,通过 Linux epoll 唤醒正在阻塞的线程,由该线程的 Looper 取消息并执行。

线程隔离:ThreadLocal


进阶:同步屏障(Sync Barrier)


实战:HandlerThread

子线程使用 Handler 的标准写法:

// 1. 创建带 Looper 的后台线程(内部已 prepare + loop)
HandlerThread handlerThread = new HandlerThread("BackgroundThread");
handlerThread.start();

// 2. 绑定子线程 Looper(主线程 new Handler 也可,消息会发到子线程)
Handler backgroundHandler = new Handler(handlerThread.getLooper()) {
    @Override
    public void handleMessage(Message msg) {
        // 运行在 BackgroundThread
    }
};

// 3. 跨线程发送
backgroundHandler.sendMessage(Message.obtain());

常见问题与避坑

(1) 内存泄漏

(2) Looper.loop() 为何不卡死主线程?

(3) 子线程创建 Handler

必须先:Looper.prepare()new Handler()Looper.loop();或直接使用 HandlerThread


一句话总结

Handler 机制通过 ThreadLocal 保证每线程唯一的 LooperMessageQueue,用 Handler 作为媒介,实现多线程向同一队列投递消息,由目标线程的 Looper 循环分发。


参考:语雀原文 Handler