在 Android 开发中,Handler、Looper、Message 和 MessageQueue 是构成线程间通信(尤其是子线程与主线程交互)的核心组件。它们共同组成了 Android 的消息驱动模型,确保 UI 操作在正确的线程执行,同时允许后台线程安全地更新界面。

1. 整体概览

  • Message:消息的载体,包含要传递的数据(如 what, arg1, arg2, obj, data)以及目标 Handler。

  • MessageQueue:消息队列,采用单链表结构,按时间顺序存储 Message,提供入队和出队操作。

  • Looper:循环器,不断从 MessageQueue 中取出消息,并分发给对应的 Handler 处理。每个线程最多只能有一个 Looper。

  • Handler:消息的发送者和处理者,负责将 Message 插入到 MessageQueue,并在 Looper 所在的线程中执行 handleMessage(Message) 回调。

它们的关系可以概括为:Handler 发送 Message → MessageQueue 存储 Message → Looper 循环取出 Message → Handler 处理 Message。

┌─────────┐   发送消息    ┌──────────────┐   入队    ┌───────────────┐
│ Handler │ ──────────▶ │ MessageQueue │ ◀─────── │   Message     │
└─────────┘              └──────────────┘          └───────────────┘
     ▲                          │
     │                          │ next() 取出
     │                          ▼
     │                   ┌──────────────┐
     └───────────────────│    Looper    │ (循环)
        dispatchMessage   └──────────────┘

2. 各组件详细解析

2.1 Message(消息)

Message 是一个实现了 Parcelable 接口的 final 类,用于封装需要传递的数据。

主要字段:

字段类型说明
whatint用户自定义的消息标识,用于区分不同类型的消息
arg1 / arg2int轻量级的整型数据,避免创建对象
objObject任意对象,传递复杂数据
dataBundle可传递更多的键值对数据
targetHandler目标 Handler,即最终处理该消息的 Handler
callbackRunnable若设置,则优先于 handleMessage 执行
whenlong消息应被处理的时间(基于 SystemClock.uptimeMillis())
nextMessage指向队列中下一条消息,构成单链表

获取 Message 实例:

推荐使用 Message.obtain() 系列方法,它们从全局消息池中复用对象,避免频繁创建导致内存抖动。

Message msg = Message.obtain();
msg.what = 1;
msg.obj = "data";

使用完后,在 handleMessage 中无需手动回收,Looper 会自动调用 recycle()。

2.2 MessageQueue(消息队列)

MessageQueue 是一个基于单链表的消息队列,按 when(触发时间)升序排列。它不是普通的 java.util.Queue,而是一个专门的实现,支持低层级的阻塞和唤醒。

关键方法:

  • boolean enqueueMessage(Message msg, long when):将消息插入队列,并根据 when 排序。若队列头部发生变化(如插入更早的消息),会通过 native 方法 nativeWake() 唤醒可能阻塞的 next()。

  • Message next():取出下一条待处理的消息。若队列为空或队首消息的触发时间未到,则调用 native 方法 nativePollOnce() 进入阻塞等待,直到被唤醒或超时。

  • void quit(boolean safe):退出消息队列,释放 Looper 循环。safe=true 时,会处理完所有未到期的消息;false 则直接丢弃所有消息。

同步屏障(Sync Barrier):

MessageQueue 支持插入一种特殊消息(target == null)作为同步屏障,用于拦截同步消息,优先处理异步消息(如 UI 绘制、输入事件)。屏障通过 postSyncBarrier() 添加,移除需调用 removeSyncBarrier()。Handler 默认发送的是同步消息,若需异步消息,可在构造时传入 async=true(API 28+ 有 Handler.createAsync)。

2.3 Looper(循环器)

Looper 负责驱动消息循环,从 MessageQueue 中不断取出消息并分发。

核心方法:

  • static void prepare():为当前线程创建唯一的 Looper 实例,并存入 ThreadLocal。

  • static void loop():启动消息循环。内部是一个 for (;;) 死循环,不断调用 queue.next() 获取消息,然后调用 msg.target.dispatchMessage(msg) 分发,最后回收消息。

  • static Looper myLooper():获取当前线程的 Looper。

  • static MessageQueue myQueue():获取当前线程的消息队列。

  • void quit() / quitSafely():结束循环。

主线程的 Looper:

主线程(ActivityThread)在 main() 方法中已经调用了 Looper.prepareMainLooper() 和 Looper.loop(),所以主线程自带消息循环,这也是为什么可以直接在主线程创建 Handler。

2.4 Handler(处理器)

Handler 是消息机制对外的接口,负责发送和处理消息。

构造方式:

  • 无参构造:使用当前线程的 Looper(若不存在则抛异常)。

  • Handler(Looper looper):指定 Looper。

  • Handler(Callback callback):设置全局回调。

  • Handler(boolean async):是否发送异步消息。

发送消息的方法:

  • sendMessage(Message msg) / sendEmptyMessage(int what)

  • sendMessageDelayed(Message msg, long delayMillis)

  • sendMessageAtTime(Message msg, long uptimeMillis)

  • post(Runnable r) / postDelayed(Runnable r, long delayMillis):将 Runnable 包装成 Message(callback 字段),发送后由该 Handler 执行。

处理消息:

dispatchMessage(Message msg) 的执行优先级:

  1. 若 msg.callback != null,则调用 msg.callback.run()(即 post 的 Runnable)。

  2. 若 Handler 的 mCallback != null,则调用 mCallback.handleMessage(msg),若返回 true 则结束。

  3. 否则调用 Handler 自身的 handleMessage(msg)。

Handler 与线程的关系:

  • Handler 本身不创建新线程,它依赖 Looper 所在的线程执行消息处理。

  • 一个线程可以有多个 Handler,但它们共享同一个 Looper 和 MessageQueue。

  • 一个 Handler 只能绑定到一个 Looper,但可以向其他线程的 MessageQueue 发送消息(跨线程通信)。


3. 工作流程详解

以主线程更新 UI 为例:

  1. 创建 Handler:在主线程创建 Handler,它自动绑定主线程的 Looper。

  2. 子线程发送消息:在子线程中调用 handler.sendMessage(msg)。Handler 内部将 msg.target 设为自己,然后调用 MessageQueue.enqueueMessage(msg, when) 将消息放入主线程的消息队列。

  3. 主线程 Looper 循环:主线程的 Looper 在 loop() 中不断调用 queue.next()。若队列中有到期消息,则返回该消息;否则阻塞等待。

  4. 分发消息:Looper 拿到消息后,调用 msg.target.dispatchMessage(msg),即调用发送该消息的 Handler 的 dispatchMessage。

  5. 处理消息:Handler 在 dispatchMessage 中根据优先级调用 handleMessage(msg) 或 Runnable.run(),此时代码运行在主线程,可以安全更新 UI。

  6. 消息回收:处理完毕后,msg.recycle() 将消息回收到消息池,供下次复用。

延时消息与阻塞唤醒:

  • 发送延时消息时,enqueueMessage 会根据 when 插入适当位置。

  • Looper 的 next() 发现队首消息尚未到期,会计算剩余时间并调用 nativePollOnce(timeout) 阻塞。

  • 当有更早的消息入队时,会调用 nativeWake() 唤醒 next() 重新检查队列。

  • 超时后 next() 自动返回消息,实现定时处理。


4. 使用场景与示例

4.1 主线程更新 UI
class MyActivity extends Activity {
    private Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 1) {
                textView.setText((String) msg.obj);
            }
        }
    };

    void doBackgroundTask() {
        new Thread(() -> {
            // 模拟耗时操作
            String result = fetchData();
            Message msg = mHandler.obtainMessage(1, result);
            mHandler.sendMessage(msg);
        }).start();
    }
}
4.2 延时执行任务
handler.postDelayed(() -> {
    // 3秒后执行
}, 3000);

// 取消延时任务
handler.removeCallbacks(runnable);
4.3 子线程间通信
class MyThread extends Thread {
    public Handler mHandler;
    public void run() {
        Looper.prepare();
        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                // 处理消息
            }
        };
        Looper.loop();
    }
}
4.4 定时任务(不推荐,可用 ScheduledExecutorService 或 AlarmManager)
handler.sendMessageDelayed(msg, 1000); // 一次性
// 循环发送:在 handleMessage 中再次 sendMessageDelayed

5. 常见问题与注意事项

5.1 内存泄漏

原因: 非静态内部类 Handler 持有外部 Activity 的引用,而 Message 持有 Handler 引用,如果消息尚未处理(如延时消息),则 Activity 无法被回收。

解决:

  • 使用静态内部类 + 弱引用(WeakReference)持有 Activity。

  • 在 onDestroy 中调用 handler.removeCallbacksAndMessages(null) 清空消息队列。

  • 使用 Handler.Callback 接口避免继承 Handler。

5.2 子线程中直接创建 Handler 报错

错误信息:Can't create handler inside thread that has not called Looper.prepare()

原因: Handler 需要绑定一个 Looper,子线程默认没有 Looper。

解决: 先调用 Looper.prepare(),再创建 Handler,最后调用 Looper.loop() 启动循环。或者使用 Handler(Looper.getMainLooper()) 绑定主线程 Looper。

5.3 为什么主线程 Looper 死循环不会导致 ANR?

ANR 是指应用未响应输入事件或广播等,主线程 Looper 循环本身是正常的,它保证了事件的及时处理。当主线程执行耗时操作(如网络请求、大量计算)时,消息无法被及时处理,才导致 ANR。Looper 阻塞在 queue.next() 时处于休眠状态,不消耗 CPU,一旦有新消息到来会立即唤醒处理。

5.4 Message 复用与 Obtaining

使用 new Message() 会频繁创建对象,造成内存抖动。Message.obtain() 从全局消息池(链表)中取出一个 Message,其 recycle() 会清空字段并放回池中。注意:在 handleMessage 中不要手动调用 recycle(),因为 Looper 会在分发后自动回收;如果使用了 obtain,也不要将消息重复使用(如再次发送)。

5.5 异步消息与同步屏障

系统为了优先处理 UI 绘制、输入事件等,会插入同步屏障,此时只有异步消息能被处理。开发者通常不需要关心,但若需要在动画等场景获得更高优先级,可以使用异步 Handler:

Handler handler = Handler.createAsync(Looper.getMainLooper());
5.6 IdleHandler

MessageQueue 提供 IdleHandler 接口,当队列空闲(没有消息需要立即处理)时执行,适用于低优先级任务(如 GC、预加载)。

Looper.myQueue().addIdleHandler(() -> {
    // 执行低优先级任务
    return false; // 返回 true 表示继续保留,false 移除
});

6. 源码简要分析

  • Handler.enqueueMessage():调用 MessageQueue.enqueueMessage(msg, uptimeMillis),同时将 msg.target = this。

  • MessageQueue.enqueueMessage():根据 when 插入链表,若插入在头部则调用 nativeWake()。

  • MessageQueue.next():无限循环,调用 nativePollOnce() 阻塞,返回消息或继续等待。若遇到同步屏障,则只处理异步消息。

  • Looper.loop():调用 queue.next(),获取消息后执行 msg.target.dispatchMessage(msg),然后 msg.recycleUnchecked()。

  • Handler.dispatchMessage():依次检查 msg.callback、mCallback、handleMessage。


7. 总结

Android 的消息机制是一个高效、灵活的设计:

  • Handler 是发送和处理消息的接口。

  • MessageQueue 负责存储和管理消息,支持延时和屏障。

  • Looper 驱动消息循环,将消息分发给对应 Handler。

  • Message 是数据载体,通过对象池优化性能。

理解它们的工作原理有助于编写稳定、高效的 Android 应用,避免线程同步和内存泄漏等问题。尽管如今有 RxJava、协程等更高级的异步方案,但 Handler 机制依然是 Android 框架的基石,许多组件(如 Activity 生命周期、View 绘制、事件分发)都建立在它之上。

一、基础

Q1:请简述 Handler 机制的工作原理

核心回答:

Handler 发送消息 → MessageQueue 按时间排序存储 → Looper 无限循环取出 → 回调 Handler 处理。

细节:

  • MessageQueue 不是队列,是单向链表,按 when(触发时间戳)排序

  • Looper.loop() 中 queue.next() 无消息时通过 native epoll 阻塞,不消耗 CPU

  • 最终 msg.target.dispatchMessage(msg),target 就是发送该消息的 Handler


Q2:一个线程可以有几个 Handler?几个 Looper?

数量原因
Looper1 个ThreadLocal 存储,prepare() 时检查重复,重复会抛异常
Handler多个多个 Handler 可以共享同一个 Looper 和 MessageQueue

源码验证:

java

// Looper.prepare()
if (sThreadLocal.get() != null) {
    throw new RuntimeException("Only one Looper may be created per thread");
}

Q3:为什么主线程的 Looper.loop() 死循环不会导致 ANR?

核心要点:

ANR 的本质是消息处理超时,不是死循环本身。

深入解析:

  1. loop() 中的 queue.next() 无消息时,会通过 native 层的 epoll_wait 阻塞线程,释放 CPU

  2. 有消息来时,native 层被唤醒,继续执行

  3. ANR 发生在 dispatchMessage() 里处理消息耗时过长(如 5s 内未响应输入)

  4. 所以死循环 + 阻塞 = 事件驱动模型,这是高性能的


Q4:MessageQueue 的数据结构是什么?

答案:单向链表(按 when 时间戳排序)

java

// Message.java
Message next;  // 链表指针
long when;     // 触发时间

为什么不用数组/堆?

  • 不需要随机访问,只取头部 → 链表 O(1)

  • 插入按时间排序,数量通常不多,O(n) 可接受

  • 内存更紧凑,且支持对象池复用


二、进阶深挖

Q5:Handler 的 sendMessageDelayed() 是怎么实现延迟的?

核心原理:

不是真的延迟发送,而是提前发送,按时间排队。

流程:

  1. 计算 when = SystemClock.uptimeMillis() + delayMillis

  2. enqueueMessage() 按 when 插入链表合适位置

  3. Looper 循环到该消息时,检查 when 是否到达:

    • 未到:计算剩余时间,通过 native nativePollOnce(ptr, timeout) 阻塞等待

    • 已到:取出处理


Q6:Handler 内存泄漏的原因?如何解决?

原因:

  • 非静态内部类 Handler 持有外部 Activity 的引用

  • 延迟消息(如 postDelayed)在 Activity 销毁时仍在 MessageQueue 中

  • Message 持有 Handler,Handler → Activity,导致 Activity 无法被回收

解决方案(3种):

java

// 方案1:静态内部类 + WeakReference
private static class SafeHandler extends Handler {
    private WeakReference<Activity> ref;
    SafeHandler(Activity activity) {
        ref = new WeakReference<>(activity);
    }
    @Override
    public void handleMessage(Message msg) {
        Activity activity = ref.get();
        if (activity != null && !activity.isFinishing()) {
            // 处理消息
        }
    }
}

// 方案2:Activity.onDestroy() 时移除消息
@Override
protected void onDestroy() {
    super.onDestroy();
    handler.removeCallbacksAndMessages(null);
}

// 方案3:使用 Lifecycle 组件自动管理

Q7:Message.obtain() 和 new Message() 有什么区别?

答案:

obtain() 从消息对象池中复用,new 会创建新对象。

源码:

java

public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0;
            sPoolSize--;
            return m;
        }
    }
    return new Message();  // 池空时才 new
}

好处: 避免频繁 GC,提升性能。Android 系统大量消息传递场景(如触摸事件、绘制)都用这个池。


Q8:post(Runnable) 和 sendMessage(Message) 有什么区别?

post(Runnable)sendMessage(Message)
本质把 Runnable 赋值给 msg.callback直接发送 Message
处理优先级高(dispatchMessage 先检查 callback)低
使用场景简单任务需要携带复杂数据

源码:

java

public final boolean post(Runnable r) {
    return sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;  // Runnable 被包装进 callback
    return m;
}

Q9:IdleHandler 是什么?使用场景?

定义:

当 MessageQueue 空闲(无消息或消息未到触发时间)时执行的回调。

使用:

java

Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
    @Override
    public boolean queueIdle() {
        // 执行低优先级任务
        return false;  // true=保留,false=执行完移除
    }
});

经典场景:

  • 启动优化:主线程空闲时加载非关键资源

  • 延迟初始化:如 LeakCanary 的初始化

  • UI 收尾:如 RecyclerView 预加载


Q10:什么是同步屏障(Sync Barrier)?

原理:

插入一个 target == null 的特殊 Message,拦截其后所有同步消息,只允许异步消息通过。

使用场景:

  • ViewRootImpl 在 scheduleTraversals() 时插入屏障

  • 确保 Choreographer 的 VSync 异步消息优先执行,避免掉帧

注意: 同步屏障是 @hide API,应用层不能直接调用。


三、实战场景题

Q11:如何在子线程中创建 Handler?

java

new Thread(() -> {
    // 子线程默认没有 Looper,必须手动创建
    Looper.prepare();
    
    Handler handler = new Handler(Looper.myLooper()) {
        @Override
        public void handleMessage(Message msg) {
            // 在子线程处理
        }
    };
    
    Looper.loop();  // 阻塞,开始消息循环
    
    // 退出时(子线程可以退出)
    // Looper.myLooper().quitSafely();
}).start();

更推荐使用 HandlerThread:

java

HandlerThread thread = new HandlerThread("bg");
thread.start();

Handler handler = new Handler(thread.getLooper());

Q12:Looper.quit() 和 quitSafely() 的区别?

quit()quitSafely()
行为立即退出,清空 MessageQueue设置退出标记,处理完已有消息后退出
延迟消息直接丢弃丢弃(只保证当前已到达的消息)
使用子线程 Looper子线程 Looper

主线程 Looper 不允许 quit,会抛异常。


Q13:主线程的 Looper 是什么时候创建的?

答案: ActivityThread.main() 中。

java

// ActivityThread.java
public static void main(String[] args) {
    Looper.prepareMainLooper();  // 创建主线程 Looper
    
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    
    Looper.loop();  // 开始循环,永不退出
}

Q14:为什么 Handler 构造方法里要求 Looper 不能为 null?

java

public Handler(Looper looper) {
    this(looper, null, false);
}

原因:

  • Handler 必须关联一个 MessageQueue 才能发消息

  • MessageQueue 来自 Looper

  • 如果当前线程没有调用 Looper.prepare(),Looper.myLooper() 返回 null

  • 直接抛异常,避免 NPE 或消息丢失


Q15:触摸事件是怎么通过 Handler 机制传递到 View 的?

流程:

  1. InputDispatcher(native)收到触摸事件

  2. 通过 JNI 将事件封装成 Message,发送到主线程的 MessageQueue

  3. Looper 取出消息,ViewRootImpl 的 WindowInputEventReceiver 处理

  4. 最终分发到 DecorView → ViewGroup → View 的 dispatchTouchEvent()

这解释了为什么在主线程执行耗时操作会导致触摸无响应——消息被堵在队列里。


四、面试速记口诀

问题一句话答案
Handler 发消息去哪?MessageQueue(链表)
Looper 干嘛的?死循环取消息,交给 Handler
为什么不会卡死?native epoll 阻塞,有消息才唤醒
延迟消息怎么实现?提前入队,按时间排序,到点才处理
内存泄漏怎么解?静态内部类 + WeakReference + removeCallbacks
子线程用 Handler?先 Looper.prepare() + Looper.loop()

更多推荐