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;
// 步骤1:获取当前线程的 Looper 和 MessageQueue
for (;;) {
Message msg = queue.next(); // 步骤2:阻塞取消息
if (msg == null) {
return; // 队列退出时返回
}
// 步骤3:分发消息
msg.target.dispatchMessage(msg);
msg.recycleUnchecked(); // 步骤4:回收 Message 到池
}
}
步骤说明
| 步骤 |
逻辑 |
说明 |
原因 |
| 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; // native 层已销毁
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
// 步骤1:native 阻塞,nextPollTimeoutMillis 为 0 不阻塞,-1 永久阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prev = null;
Message msg = mMessages; // 链表头
if (msg != null && msg.target == null) {
// 步骤2:遇到屏障消息,跳过普通消息,只处理异步消息
do {
prev = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 步骤3:未到执行时间,计算等待时长
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 步骤4:到时间了,取出消息
mBlocked = false;
if (prev != null) prev.next = msg.next;
else mMessages = msg.next;
msg.next = null;
msg.markInUse();
return msg;
}
} else {
nextPollTimeoutMillis = -1; // 无消息,永久阻塞
}
// ... IdleHandler 处理
}
}
}
步骤说明
| 步骤 |
逻辑 |
说明 |
原因 |
| 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
// post(Runnable) 本质是发送一个 callback 类型的 Message
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;
}
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; // 绑定 Handler,用于 loop 时回调
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;
// 步骤1:队列空 或 新消息最早
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked; // 若正在阻塞,需要唤醒
} else {
// 步骤2:按 when 插入到合适位置
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); // 步骤3:唤醒 native 层
}
}
return true;
}
步骤说明
| 步骤 |
逻辑 |
说明 |
原因 |
| 1 |
`p == null |
|
when < p.when` |
| 2 |
遍历链表 |
按 when 升序找到插入位置并插入 |
保证消息按时间顺序执行,延时消息在正确时机被取出 |
| 3 |
nativeWake(mPtr) |
若在阻塞,唤醒 native 层 |
next() 在 nativePollOnce 中休眠,新消息入队后必须唤醒才能及时处理,否则会一直阻塞 |
八、Handler.dispatchMessage():分发消息
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 步骤1:post(Runnable) 的情况,直接执行 callback
handleCallback(msg);
} else {
if (mCallback != null) {
// 步骤2:Handler 设置了 Callback,先交给它处理
if (mCallback.handleMessage(msg)) return;
}
// 步骤3:最终交给 handleMessage
handleMessage(msg);
}
}
private static void handleCallback(Message msg) {
msg.callback.run(); // 执行 Runnable
}
步骤说明
| 步骤 |
逻辑 |
说明 |
原因 |
| 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 对象池
// Message 复用,避免频繁 new
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,支持对象池复用 |