Android Handler 机制源码分析

10 阅读6分钟

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));
}

步骤说明

步骤逻辑说明原因
1sThreadLocal.get() != null检查当前线程是否已有 Looper避免同一线程重复 prepare,导致多个 Looper 竞争同一 MessageQueue,引发消息错乱
2抛异常一个线程只能有一个 Looper保证线程与 Looper 一一对应,消息路由清晰
3new Looper(quitAllowed)创建 Looper,内部会创建 MessageQueueLooper 需要持有自己的消息队列,用于存储待处理消息
4sThreadLocal.set(looper)将 Looper 存入当前线程的 ThreadLocal线程隔离,每个线程只能访问自己的 Looper,实现多线程各自的消息循环

三、Looper 构造函数

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);  // 创建消息队列
    mThread = Thread.currentThread();        // 绑定当前线程
}

步骤说明

步骤逻辑说明原因
1new MessageQueue(quitAllowed)创建该 Looper 的消息队列每个 Looper 需要独立队列存储消息,quitAllowed 控制主线程 Looper 是否允许退出
2mThread = 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 到池
    }
}

步骤说明

步骤逻辑说明原因
1myLooper()me.mQueue获取当前线程的 Looper 和 MessageQueue通过 ThreadLocal 取本线程 Looper,保证 loop 操作的是本线程的队列
2queue.next()阻塞取下一个消息,无消息时线程休眠有消息时立即处理,无消息时休眠避免空转耗 CPU,由 enqueueMessage 的 nativeWake 唤醒
3msg.target.dispatchMessage(msg)调用 Handler 的 dispatchMessage 处理消息target 即发送该消息的 Handler,保证消息由正确的 Handler 处理
4msg.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 处理
        }
    }
}

步骤说明

步骤逻辑说明原因
1nativePollOnce(ptr, nextPollTimeoutMillis)在 native 层阻塞,0 不阻塞,-1 永久阻塞使用 epoll 等机制在 native 层休眠,比 Java 层 wait 更高效,且可与 native 事件(如输入、绘制)统一调度
2msg.target == null消息屏障,跳过普通消息,只处理异步消息用于 View 绘制等需要优先执行的场景,屏障期间普通消息暂不处理,保证 UI 流畅
3now < msg.when未到执行时间,计算需要等待的毫秒数支持延时消息,避免过早执行,nextPollTimeoutMillis 让 native 层精确唤醒
4取出 msg、更新链表从链表移除该消息并返回消息已被消费,需从队列移除,防止重复处理
5nextPollTimeoutMillis = -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);
}

步骤说明

步骤逻辑说明原因
1post(Runnable)封装成 Message,callback = r统一用 Message 表示任务,Runnable 作为 callback 在 dispatchMessage 时执行
2Message.obtain()从对象池取 Message避免频繁 new Message,降低 GC 压力
3sendMessageDelayed计算 uptimeMillis = now + delayMillis将相对延时转为绝对时间戳,便于队列按时间排序
4msg.target = this指定处理该消息的 Handlerloop 时通过 target 找到发送者,将消息分发给正确的 Handler
5queue.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;
}

步骤说明

步骤逻辑说明原因
1p == null || when < p.when队列为空或新消息最早,插到链表头新消息成为下一个被取出的消息,需放队头;next() 从 mMessages 取,队头即最早消息
2遍历链表when 升序找到插入位置并插入保证消息按时间顺序执行,延时消息在正确时机被取出
3nativeWake(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
}

步骤说明

步骤逻辑说明原因
1msg.callback != nullpost(Runnable) 发送的,执行 msg.callback.run()post 时把 Runnable 存到 callback,此处直接执行,无需再建 Message 子类
2mCallback != null若 Handler 有 Callback,先由 Callback 处理支持外部注入处理逻辑,若返回 true 表示已处理,不再调用 handleMessage
3handleMessage(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,支持对象池复用