Android Handler 机制源码分析
一、整体结构
Handler 机制
├── Handler - 发送消息、处理消息
├── Message - 消息载体
├── MessageQueue - 消息队列(单链表,按 when 排序)
├── Looper - 循环取消息并分发
└── ThreadLocal - 每个线程持有自己的 Looper
二、Looper.prepare():初始化 Looper
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
步骤说明
| 步骤 | 逻辑 | 说明 | 原因 |
|---|
| 1 | sThreadLocal.get() != null | 检查当前线程是否已有 Looper | 避免同一线程重复 prepare,导致多个 Looper 竞争同一 MessageQueue,引发消息错乱 |
| 2 | 抛异常 | 一个线程只能有一个 Looper | 保证线程与 Looper 一一对应,消息路由清晰 |
| 3 | new Looper(quitAllowed) | 创建 Looper,内部会创建 MessageQueue | Looper 需要持有自己的消息队列,用于存储待处理消息 |
| 4 | sThreadLocal.set(looper) | 将 Looper 存入当前线程的 ThreadLocal | 线程隔离,每个线程只能访问自己的 Looper,实现多线程各自的消息循环 |
三、Looper 构造函数
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
步骤说明
| 步骤 | 逻辑 | 说明 | 原因 |
|---|
| 1 | new MessageQueue(quitAllowed) | 创建该 Looper 的消息队列 | 每个 Looper 需要独立队列存储消息,quitAllowed 控制主线程 Looper 是否允许退出 |
| 2 | mThread = Thread.currentThread() | 记录 Looper 所在线程 | 用于校验 Handler 是否在正确线程处理消息,以及 debug 时定位线程 |
四、Looper.loop():消息循环
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next();
if (msg == null) {
return;
}
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
}
步骤说明
| 步骤 | 逻辑 | 说明 | 原因 |
|---|
| 1 | myLooper()、me.mQueue | 获取当前线程的 Looper 和 MessageQueue | 通过 ThreadLocal 取本线程 Looper,保证 loop 操作的是本线程的队列 |
| 2 | queue.next() | 阻塞取下一个消息,无消息时线程休眠 | 有消息时立即处理,无消息时休眠避免空转耗 CPU,由 enqueueMessage 的 nativeWake 唤醒 |
| 3 | msg.target.dispatchMessage(msg) | 调用 Handler 的 dispatchMessage 处理消息 | target 即发送该消息的 Handler,保证消息由正确的 Handler 处理 |
| 4 | msg.recycleUnchecked() | 将 Message 回收到对象池 | 减少 GC,Message 使用频繁,复用可显著降低内存分配和回收开销 |
五、MessageQueue.next():取消息
Message next() {
final long ptr = mPtr;
if (ptr == 0) return null;
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prev = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
do {
prev = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
mBlocked = false;
if (prev != null) prev.next = msg.next;
else mMessages = msg.next;
msg.next = null;
msg.markInUse();
return msg;
}
} else {
nextPollTimeoutMillis = -1;
}
}
}
}
步骤说明
| 步骤 | 逻辑 | 说明 | 原因 |
|---|
| 1 | nativePollOnce(ptr, nextPollTimeoutMillis) | 在 native 层阻塞,0 不阻塞,-1 永久阻塞 | 使用 epoll 等机制在 native 层休眠,比 Java 层 wait 更高效,且可与 native 事件(如输入、绘制)统一调度 |
| 2 | msg.target == null | 消息屏障,跳过普通消息,只处理异步消息 | 用于 View 绘制等需要优先执行的场景,屏障期间普通消息暂不处理,保证 UI 流畅 |
| 3 | now < msg.when | 未到执行时间,计算需要等待的毫秒数 | 支持延时消息,避免过早执行,nextPollTimeoutMillis 让 native 层精确唤醒 |
| 4 | 取出 msg、更新链表 | 从链表移除该消息并返回 | 消息已被消费,需从队列移除,防止重复处理 |
| 5 | nextPollTimeoutMillis = -1 | 无消息时永久阻塞 | 队列空时线程休眠,等新消息入队时由 nativeWake 唤醒,避免忙等 |
六、Handler 发送消息:sendMessage / post
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) delayMillis = 0;
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) return false;
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
return queue.enqueueMessage(msg, uptimeMillis);
}
步骤说明
| 步骤 | 逻辑 | 说明 | 原因 |
|---|
| 1 | post(Runnable) | 封装成 Message,callback = r | 统一用 Message 表示任务,Runnable 作为 callback 在 dispatchMessage 时执行 |
| 2 | Message.obtain() | 从对象池取 Message | 避免频繁 new Message,降低 GC 压力 |
| 3 | sendMessageDelayed | 计算 uptimeMillis = now + delayMillis | 将相对延时转为绝对时间戳,便于队列按时间排序 |
| 4 | msg.target = this | 指定处理该消息的 Handler | loop 时通过 target 找到发送者,将消息分发给正确的 Handler |
| 5 | queue.enqueueMessage() | 按 when 插入 MessageQueue | 保证延时消息和普通消息按时间顺序执行 |
七、MessageQueue.enqueueMessage():入队
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) throw new IllegalArgumentException("Message must have a target.");
if (msg.isInUse()) throw new IllegalStateException("msg already in use");
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = false;
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) break;
}
msg.next = p;
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
步骤说明
| 步骤 | 逻辑 | 说明 | 原因 |
|---|
| 1 | p == null || when < p.when | 队列为空或新消息最早,插到链表头 | 新消息成为下一个被取出的消息,需放队头;next() 从 mMessages 取,队头即最早消息 |
| 2 | 遍历链表 | 按 when 升序找到插入位置并插入 | 保证消息按时间顺序执行,延时消息在正确时机被取出 |
| 3 | nativeWake(mPtr) | 若在阻塞,唤醒 native 层 | next() 在 nativePollOnce 中休眠,新消息入队后必须唤醒才能及时处理,否则会一直阻塞 |
八、Handler.dispatchMessage():分发消息
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) return;
}
handleMessage(msg);
}
}
private static void handleCallback(Message msg) {
msg.callback.run();
}
步骤说明
| 步骤 | 逻辑 | 说明 | 原因 |
|---|
| 1 | msg.callback != null | post(Runnable) 发送的,执行 msg.callback.run() | post 时把 Runnable 存到 callback,此处直接执行,无需再建 Message 子类 |
| 2 | mCallback != null | 若 Handler 有 Callback,先由 Callback 处理 | 支持外部注入处理逻辑,若返回 true 表示已处理,不再调用 handleMessage |
| 3 | handleMessage(msg) | 子类重写的方法,处理业务逻辑 | 作为默认处理入口,子类重写以实现具体业务 |
九、Message 对象池
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();
}
void recycleUnchecked() {
flags = FLAG_IN_USE;
next = sPool;
sPool = this;
sPoolSize++;
}
步骤说明
| 步骤 | 逻辑 | 说明 | 原因 |
|---|
| obtain | 从 sPool 取头节点 | 有可用 Message 则复用 | 主线程消息量大,复用可减少 GC,提升性能 |
| recycleUnchecked | 将当前 Message 放回 sPool 头 | 用完后回收到池 | 与 obtain 配合形成复用链,避免频繁创建和销毁 |
十、整体流程图
主线程启动
│
├─ Looper.prepare() ──→ 创建 Looper、MessageQueue,存入 ThreadLocal
│
└─ Looper.loop() ──→ 死循环
│
└─ queue.next() ──→ 阻塞取消息
│
└─ msg.target.dispatchMessage(msg) ──→ Handler 处理
Handler.post/sendMessage
│
├─ Message.obtain() ──→ 从池取 Message
├─ msg.target = this
├─ msg.callback = runnable (post 时)
│
└─ queue.enqueueMessage() ──→ 按 when 插入队列
│
└─ nativeWake() ──→ 若在阻塞则唤醒
十一、小结
| 组件 | 作用 |
|---|
| Looper | 绑定线程,持有 MessageQueue,loop() 循环取消息并分发 |
| MessageQueue | 单链表按 when 排序,native 层实现阻塞/唤醒 |
| Handler | 发送消息、处理消息,绑定到创建它的 Looper |
| Message | 消息载体,含 target、callback、when,支持对象池复用 |