核心架构概览
Handler/Looper 机制的本质是 "线程间消息传递 + 消息队列 + 事件循环" 的组合。
css
[生产者线程] → Handler → MessageQueue → Looper → [消费者线程]
1. 核心组件深度解析
1.1 Looper - 消息泵
java
public final class Looper {
// 每个线程独享的ThreadLocal存储
private static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
final MessageQueue mQueue; // 关联的消息队列
final Thread mThread; // 所属线程
// 准备当前线程的Looper
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
// 核心循环 - 消息泵
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) { // 无限循环
Message msg = queue.next(); // 可能阻塞
if (msg == null) return; // 只有队列退出时返回null
// 关键:在目标线程执行消息处理!
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
}
}
关键设计:ThreadLocal
- 每个线程有自己独立的 Looper 实例
sThreadLocal
保证线程隔离,不同线程访问得到不同对象
1.2 MessageQueue - 消息仓库
java
public final class MessageQueue {
private long mPtr; // Native层的消息队列指针
// 核心:消息入队(线程安全)
boolean enqueueMessage(Message msg, long when) {
synchronized (this) { // 🔐 关键锁!
msg.markInUse();
msg.when = when;
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
// 插入队列头部
msg.next = p;
mMessages = msg;
} else {
// 按执行时间排序插入
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) break;
}
msg.next = p;
prev.next = msg;
}
// 唤醒可能阻塞的next()方法
if (needWake) nativeWake(mPtr);
}
return true;
}
// 核心:消息出队
Message next() {
for (;;) {
// 可能阻塞在nativePollOnce
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 查找可执行的消息
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 同步屏障处理
do {
prevMsg = 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 {
// 找到可执行消息,从队列移除
mMessages = msg.next;
msg.next = null;
return msg;
}
} else {
nextPollTimeoutMillis = -1;
}
}
}
}
}
1.3 Handler - 消息代理
java
public class Handler {
final Looper mLooper;
final MessageQueue mQueue;
public Handler() {
this(Looper.myLooper()); // 默认绑定当前线程Looper
}
public Handler(Looper looper) {
mLooper = looper;
mQueue = looper.mQueue;
}
// 发送消息的核心方法
public boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
return sendMessageAtTime(msg,
SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; // 关键:设置消息的处理者
return queue.enqueueMessage(msg, uptimeMillis);
}
// 消息分发
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 处理Runnable
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) return;
}
// 最终调用到我们重写的handleMessage
handleMessage(msg);
}
}
}
2. 线程切换的完整流程
场景:子线程 → 主线程更新UI
java
// 1. 主线程初始化时
class MainThread {
void onCreate() {
// 系统已调用:Looper.prepareMainLooper();
// 系统已调用:Looper.loop();
mMainHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 这里在主线程执行!
updateUI(msg.obj);
}
};
}
}
// 2. 子线程发送消息
class WorkerThread {
void doWork() {
// 在子线程执行网络请求
String data = fetchDataFromNetwork();
// 关键:通过主线程的Handler发送消息
Message msg = mMainHandler.obtainMessage(WHAT_UPDATE_UI, data);
mMainHandler.sendMessage(msg);
}
}
详细执行流程:
-
子线程调用
mMainHandler.sendMessage(msg)
- 获取到主线程 Handler 的引用
- 创建 Message 并设置数据
-
Handler.enqueueMessage()
- 设置
msg.target = this
(this 是主线程的 Handler) - 调用
mQueue.enqueueMessage(msg, time)
- 设置
-
MessageQueue 线程安全入队
- 获取 MessageQueue 的锁 (
synchronized (this)
) - 按执行时间将消息插入到合适位置
- 如果 Looper 正在阻塞,调用
nativeWake()
唤醒
- 获取 MessageQueue 的锁 (
-
主线程 Looper.loop() 处理
queue.next()
从队列取出消息- 调用
msg.target.dispatchMessage(msg)
- 这里
msg.target
就是主线程的 Handler
-
在主线程执行
dispatchMessage()
在主线程调用- 最终执行到
handleMessage()
更新 UI
3. 关键技术实现
3.1 线程绑定机制
java
// Handler 构造时绑定目标线程
public Handler(Looper looper) {
mLooper = looper;
mQueue = looper.mQueue;
mThread = looper.mThread; // 保存目标线程引用
}
// 可以在任何线程调用,但会在目标线程执行
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
3.2 消息池优化
java
// 避免频繁创建Message对象
public final Message obtainMessage() {
return Message.obtain(this);
}
// Message池实现
public final class Message implements Parcelable {
private static Message sPool; // 回收的消息对象池
private static int sPoolSize = 0;
Message next; // 链表结构
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
public void recycle() {
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
}
3.3 Native 层支持
MessageQueue 在 Native 层使用 epoll
机制实现高效阻塞/唤醒:
cpp
// native/libmessagequeue/MessageQueue.cpp
void NativeMessageQueue::pollOnce(int timeoutMillis) {
mLooper->pollOnce(timeoutMillis);
}
// frameworks/base/libs/utils/Looper.cpp
int Looper::pollOnce(int timeoutMillis) {
int result = 0;
for (;;) {
// 使用epoll等待事件
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
if (eventCount == 0) {
result = ALOOPER_POLL_TIMEOUT;
break;
}
// 处理唤醒事件
for (int i = 0; i < eventCount; i++) {
if (eventItems[i].data.fd == mWakeEventFd) {
awoken(); // 读取唤醒事件
}
}
}
return result;
}
4. 并发安全设计
4.1 锁策略
java
public final class MessageQueue {
// 入队操作加锁
boolean enqueueMessage(Message msg, long when) {
synchronized (this) { // 使用对象内置锁
// 临界区代码
}
}
// 出队操作同样加锁
Message next() {
synchronized (this) {
// 临界区代码
}
}
}
4.2 线程封闭
通过 ThreadLocal 实现线程数据隔离:
java
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
// 每个线程访问得到不同的Looper实例
Looper.myLooper() == sThreadLocal.get()
5. 总结
Handler/Looper 实现线程切换的核心在于:
- 消息路由:Handler 作为消息的路由器,知道目标线程的消息队列
- 队列隔离:每个线程有独立的消息队列,实现线程数据隔离
- 事件循环:Looper 在目标线程不断从队列取消息执行
- 线程安全:通过 synchronized 保证多线程入队安全
- 高效阻塞:Native 层使用 epoll 实现高效等待/唤醒
这种设计实现了 "在哪里定义,在哪里执行" 的线程切换效果,是 Android 异步编程的基石。