Handler 实现线程切换的核心在于消息的发送和处理分离:发送消息的线程与处理消息的线程可以不同,通过共享的 MessageQueue 和 Looper 机制实现跨线程通信。下面我将结合关键源码详细解析这一原理。
目录
一、核心组件关系
Handler 机制主要由四个核心类组成:
- Handler:消息发送者和处理者
- Message:消息载体
- MessageQueue:消息队列,线程安全的数据结构
- Looper:消息循环器,驱动消息处理
二、线程切换实现原理
1. Looper 的线程绑定机制
每个线程要使用 Handler 必须先创建 Looper,通过 ThreadLocal 实现线程隔离:
java
// Looper.java
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)); // 创建并绑定到当前线程
}
主线程的 Looper 在 ActivityThread.main() 中初始化:
java
// ActivityThread.java
public static void main(String[] args) {
Looper.prepareMainLooper(); // 创建主线程Looper
Looper.loop(); // 启动消息循环
}
2. Handler 的创建与线程绑定
Handler 构造时会绑定当前线程的 Looper:
java
// Handler.java
public Handler(@Nullable Callback callback, boolean async) {
mLooper = Looper.myLooper(); // 获取当前线程的Looper
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; // 获取消息队列
}
3. 消息发送过程(跨线程)
当子线程通过 Handler 发送消息时:
java
// Handler.java
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; // 设置处理该消息的Handler
return queue.enqueueMessage(msg, uptimeMillis); // 入队
}
关键点:无论从哪个线程调用 sendMessage,最终都会将消息放入 Handler 绑定的 Looper 的消息队列中。
4. 消息队列的线程安全
MessageQueue 通过 synchronized 保证线程安全:
java
// MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
synchronized (this) { // 同步锁
// 按时间顺序插入消息链表
if (needWake) {
nativeWake(mPtr); // 唤醒目标线程的Looper
}
}
return true;
}
5. 消息处理过程(目标线程)
Looper 在目标线程中循环处理消息:
java
// Looper.java
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // 可能阻塞
if (msg == null) {
return;
}
msg.target.dispatchMessage(msg); // 分发到Handler处理
msg.recycleUnchecked();
}
}
消息最终会回到发送时指定的 Handler 处理:
java
// Handler.java
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg); // 处理Runnable
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); // 最终处理
}
}
三、底层唤醒机制
当消息队列为空时,Looper 会通过 epoll 机制进入休眠状态:
java
// MessageQueue.java
Message next() {
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis); // Native层阻塞
// ...处理消息
}
}
当有新消息插入队列时,会调用 nativeWake 唤醒:
java
void Looper::wake() {
uint64_t inc = 1;
write(mWakeEventFd, &inc, sizeof(uint64_t)); // 向eventfd写入数据
}
四、完整线程切换流程示例
- 主线程:创建 Handler 并绑定主线程 Looper
- 子线程:调用 handler.sendMessage(msg)
- 跨线程:消息被放入主线程的 MessageQueue
- 主线程:Looper.loop() 从队列取出消息并处理
java
// 主线程
Handler mainHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
// 在主线程处理消息
}
};
// 子线程
new Thread(() -> {
Message msg = mainHandler.obtainMessage();
mainHandler.sendMessage(msg); // 发送到主线程
}).start();
五、关键设计要点
- 线程隔离:通过 ThreadLocal 确保每个线程有独立的 Looper
- 线程安全:MessageQueue 使用 synchronized 保证并发安全
- 高效唤醒:基于 Linux 的 epoll 机制实现精准唤醒
- 消息优先级:支持同步屏障和异步消息优先处理
- 内存管理:Message 对象池减少内存分配开销
这种设计使得 Android 能够高效安全地实现跨线程通信,同时保持较低的功耗
0 条评论