Android学习总结之handler源码级
一、核心类关系与线程绑定(ThreadLocal 的核心作用)
1. Looper 与 ThreadLocal 的绑定
每个线程的 Looper 实例通过 ThreadLocal<Looper> sThreadLocal
存储,确保线程隔离:
public final class Looper {
// 线程本地存储,每个线程独有一个 Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// 主线程 Looper(ActivityThread 中创建)
static Looper sMainLooper;
final MessageQueue mQueue; // 关联的消息队列
final Thread mThread; // 绑定的线程
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread(); // 记录当前线程
}
// 线程首次调用,创建 Looper 并存储到 ThreadLocal
public static void prepare() {
prepare(false); // quitAllowed 默认 false(主线程不允许退出)
}
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
}
// 获取当前线程的 Looper
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
}
- 主线程:Android 框架在
ActivityThread.main()
中自动调用Looper.prepareMainLooper()
和Looper.loop()
,无需手动处理。 - 子线程:必须手动调用
Looper.prepare()
(创建 Looper 和 MessageQueue)和Looper.loop()
(启动消息循环),否则 Handler 无可用 Looper 会报错。
2. Handler 的构造与 Looper 关联
Handler 实例必须与一个 Looper 绑定,默认使用当前线程的 Looper(通过 Looper.myLooper()
获取):
public class Handler {
final Looper mLooper; // 关联的 Looper
final MessageQueue mQueue; // Looper 的消息队列
final Callback mCallback; // 消息回调
public Handler() {
this(null, false);
}
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: "
+ klass.getCanonicalName());
}
}
mLooper = Looper.myLooper(); // 获取当前线程的 Looper(必须已 prepare)
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; // 关联消息队列
mCallback = callback;
mAsynchronous = async;
}
}
- 若子线程未调用
Looper.prepare()
,创建 Handler 时会抛出RuntimeException
,这就是子线程必须先准备 Looper 的原因。
二、消息发送:从 Handler 到 MessageQueue 的入队
1. Message 的创建与重用(消息池机制)
Message 优先从消息池获取,避免频繁 GC:
public final class Message implements Parcelable {
// 消息池头节点(静态,所有线程共享)
private static Message sPool;
// 消息池大小(最大 50 个)
private static int sPoolSize = 0;
// 下一个可用消息(形成单链表)
@UnsupportedAppUsage
Message next;
// 从消息池获取消息
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() {
if (isInUse()) { // 确保未被使用
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycle();
}
private void recycle() {
if (mRecycled) { // 已回收过则不再处理
return;
}
mRecycled = true;
if (sPoolSize < MAX_POOL_SIZE) { // 最大 50 个
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
- 优势:减少对象创建开销,提升性能,尤其适合高频发送消息的场景。
2. MessageQueue 的入队逻辑(enqueueMessage)
消息按 msg.when
(执行时间)排序插入,形成一个 非严格 FIFO 的有序单链表:
boolean enqueueMessage(Message msg, long when) {
msg.target = this; // target 指向发送消息的 Handler
msg.workSourceUid = ThreadLocalWorkSource.getUid();
synchronized (this) { // 加锁保证线程安全
if (mQuitting) { // 队列已退出,回收消息
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
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 { // 找到合适位置插入(按 when 升序)
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) { // 遍历链表找到插入点
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
// 若队列阻塞且需要唤醒(如 Looper.loop() 在等待),通过 native 方法唤醒
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
- 关键点:
synchronized (this)
确保多线程安全,不同线程的 Handler 发送消息到同一 MessageQueue 时不会冲突。- 消息按
when
排序,而非严格的 FIFO,支持延迟消息(如postDelayed
)。 - 异步消息(
msg.setAsynchronous(true)
)可打断同步消息的等待,优先处理。
三、消息循环:Looper.loop () 的无限循环
1. loop () 方法核心逻辑
public static void loop() {
final Looper me = myLooper(); // 获取当前线程的 Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; // 关联的消息队列
// 确保主线程 Looper 不会被 GC 回收(Binder 机制相关)
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) { // 无限循环,直到队列退出
Message msg = queue.next(); // 取出消息(可能阻塞)
if (msg == null) { // 队列退出(msg.next == null)
return; // 退出循环
}
// 处理消息:通过 msg.target(Handler)分发
msg.target.dispatchMessage(msg);
// 回收消息到池中(非必须,系统自动处理)
msg.recycleUnchecked();
}
}
2. MessageQueue.next () 的阻塞与唤醒
next()
是消息循环的核心,通过 native 层实现阻塞等待:
Message next() {
final long ptr = mPtr; // 指向 native 层的 MessageQueue 实例
int pendingIdleHandlerCount = -1; // 首次调用时初始化为 -1
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// native 层阻塞等待消息,直到有消息或被唤醒
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 检查是否有消息待处理
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.when > now) { // 消息未到执行时间,计算延迟
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else { // 有可执行的消息,取出头节点
msg = mMessages;
mMessages = msg.next;
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
nextPollTimeoutMillis = -1; // 下次立即 poll
}
if (msg != null) { // 有消息,返回给 Looper
return msg;
}
// 无消息,检查是否退出
mQuitting = true;
return null;
}
}
}
- 阻塞原理:通过
nativePollOnce
进入内核等待,当消息入队时(enqueueMessage
中调用nativeWake
)被唤醒。 - 退出条件:调用
Looper.quit()
或Looper.quitSafely()
时,mQuitting
设为 true,next()
返回 null,loop()
终止。
四、消息处理:Handler.dispatchMessage 的分发逻辑
消息最终由发送它的 Handler 处理,分发流程如下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) { // 优先处理 Message 的 Runnable(post(Runnable))
handleCallback(msg);
} else if (mCallback != null) { // 其次处理 Handler 的 Callback
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); // 最后调用用户重写的 handleMessage()
}
private static void handleCallback(Message message) {
message.callback.run(); // 执行 post(Runnable) 传入的 Runnable
}
- 三种处理方式:
- Message 自带的 Runnable:通过
post(Runnable)
发送的消息,直接执行Runnable.run()
。 - Handler 的 Callback:通过构造函数传入的
Callback
,优先级高于handleMessage
。 - 用户重写的 handleMessage:最常用的消息处理逻辑。
- Message 自带的 Runnable:通过
五、子线程使用 Handler 的完整流程(源码级示例)
// 子线程类
class WorkerThread extends Thread {
private Handler mHandler;
private Looper mLooper;
// 获取 Handler(供外部发送消息)
public Handler getHandler() {
return mHandler;
}
@Override
public void run() {
// 1. 准备 Looper(创建 Looper 和 MessageQueue)
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper(); // 保存当前线程的 Looper
mHandler = new Handler() { // 创建 Handler 关联当前 Looper
@Override
public void handleMessage(Message msg) {
// 处理消息(子线程中执行)
processMessage(msg);
if (msg.what == MSG_QUIT) { // 退出消息
mLooper.quit(); // 停止消息循环
}
}
};
notify(); // 通知主线程 Handler 已准备好
}
// 2. 启动消息循环
Looper.loop();
}
}
// 主线程使用子线程 Handler
public class MainActivity extends AppCompatActivity {
private WorkerThread mWorkerThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mWorkerThread = new WorkerThread();
mWorkerThread.start();
synchronized (mWorkerThread) {
try {
mWorkerThread.wait(); // 等待子线程准备好 Handler
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 向子线程发送消息
Message msg = Message.obtain();
msg.what = WorkerThread.MSG_WORK;
mWorkerThread.getHandler().sendMessage(msg);
// 发送退出消息
mWorkerThread.getHandler().sendMessage(Message.obtain(null, WorkerThread.MSG_QUIT));
}
}
- 关键步骤:
- 子线程中调用
Looper.prepare()
创建 Looper 和 MessageQueue。 - 创建 Handler 时自动关联当前 Looper(因在
prepare()
之后调用)。 - 调用
Looper.loop()
启动消息循环,处理外部发送的消息。 - 通过
Looper.quit()
终止循环,避免子线程阻塞。
- 子线程中调用
六、常见问题源码级解析
1. 为什么主线程可以直接创建 Handler?
- 主线程(ActivityThread 所在线程)在启动时,框架自动调用了:
public static void main(String[] args) { // ... Looper.prepareMainLooper(); // 准备主线程 Looper ActivityThread thread = new ActivityThread(); thread.attach(false, startSeq); Looper.loop(); // 启动主线程消息循环 }
因此主线程的 Looper 已存在,无需手动调用prepare()
。
2. MessageQueue 真的是 “队列” 吗?
- 从数据结构看,它是一个 单链表,而非传统的 FIFO 队列。消息按
msg.when
排序插入,保证按时间顺序执行,支持延迟消息。
3. postDelayed 不准时的根本原因?
postDelayed
计算延迟的基准时间是SystemClock.uptimeMillis()
(系统启动后非休眠时间),若设备休眠,休眠时间不计入延迟,导致实际执行时间晚于预期。- 此外,消息需等待前面的消息处理完成,若主线程被阻塞(如耗时操作),延迟消息会被阻塞。
4. 多线程发送消息到同一 MessageQueue 为何线程安全?
MessageQueue.enqueueMessage
使用synchronized (this)
加锁,确保同一时间只有一个线程操作队列,避免并发问题。
七、Handler 机制的设计精髓
- 线程隔离:通过 ThreadLocal 保证每个线程独有 Looper 和 MessageQueue,避免资源竞争。
- 消息池:重用 Message 对象,减少内存分配和 GC 压力,提升性能。
- 有序调度:按时间排序的消息链表,支持延迟和异步消息,灵活控制执行顺序。
- 阻塞与唤醒:通过 native 层实现高效的等待与唤醒,避免 CPU 空转。
总结
Handler 机制的核心是通过 Looper(消息循环)、MessageQueue(有序消息链表)、Handler(消息收发器) 的协作,实现线程间的安全通信。源码中大量使用 ThreadLocal、synchronized、单链表等技术,确保线程隔离、数据安全和性能优化。深入理解这些细节,能帮助开发者更好地处理线程通信、避免内存泄漏(如非静态内部类 Handler 导致的 Activity 泄漏),并在复杂场景中灵活运用 Handler 机制。