Skip to content

消息机制总览

以一次 Handler.post 为主线,追踪消息从 Java 入队、JNI 桥接到 native Looper 阻塞和回调分发的真实源码路径。

基于android-17.0.0_r1
AndroidHandlerLooperMessageQueue源码阅读

消息机制总览 ​

本文面向已经能阅读 Java 方法、JNI 注册表和 C++ 类,理解线程与文件描述符基本概念的读者。建议先读 Java调用链 了解跨线程回调的阅读方法,再读 搜索调用链 练习从方法名追到 native 消费者。

本文只回答一个具体问题:一个线程已经进入 Looper.loop() 并在等待,另一个执行路径调用 Handler.post(r) 后,Runnable 如何回到绑定线程执行?答案不是“Handler 把任务放进队列”这么简单,而是一条跨层路径:Handler 创建带 callback 的 Message,MessageQueue 按 uptimeMillis 插入链表并决定是否唤醒,JNI 将 nativePollOnce 和 nativeWake 转给 NativeMessageQueue,native Looper 用 eventfd/epoll 睡眠或唤醒,Java Looper 取回消息后再调用 Handler.dispatchMessage。

读完后,你应能从 post() 定位到入队函数,解释消息为何需要 target,说明队列何时进入 native 阻塞,区分“入队成功”与“已经执行”,并能从退出、异常和清理路径继续搜索。

1. 对象边界 ​

四个对象解决的是四个不同问题:Handler 保存发送者和消费者的绑定关系;Message 保存一次投递的载荷和目标;MessageQueue 保存待处理顺序与唤醒状态;Looper 在所属线程中反复取消息并调用目标。native Looper 不是 Java Looper 的父类,它只负责把消息队列的等待连接到文件描述符事件。

对象所有者关键状态消费者
Handler创建它的应用或系统组件mLooper、mQueue、mCallback、mAsynchronousdispatchMessage()
Message当前一次队列投递target、callback、when、nextLooper.loop()
MessageQueue一个 Java LoopermMessages、mQuitting、mBlocked、mPtrnext()
Java Looper绑定线程ThreadLocal、mQueue、循环状态loopOnce()
native LooperNativeMessageQueueeventfd、epoll、fd requestspollOnce()

一个 Looper 只有一个 MessageQueue,但可以有多个 Handler。因此消息不是通过“属于哪个线程”的字段分发,而是通过 Message.target 找回具体 Handler;线程归属由 Handler 保存的 Looper 间接确定。

2. 线程准备 ​

消息链开始前,线程必须先拥有 Looper。Looper.prepare() 使用静态 ThreadLocal,同一线程第二次调用会抛异常;它只创建 Looper 和 MessageQueue,不会自动开始循环。

源码文件:frameworks/base/core/java/android/os/Looper.java

相关函数:prepare()、prepare(boolean)

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

new Looper(quitAllowed) 内部创建 MessageQueue;应用主线程的 Looper 通常由运行环境准备,业务代码只拿 Looper.getMainLooper()。自建工作线程则需要显式 prepare(),随后调用 loop(),否则 post() 只能入队而没有消费者。

源码文件:frameworks/base/core/java/android/os/Looper.java

相关函数:Looper 构造函数

java
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

3. 消息入队 ​

Handler.post(r) 并不直接保存一个 Runnable 到队列。它先通过 getPostMessage(r) 把 Runnable 放进 Message.callback,再沿 sendMessageDelayed、sendMessageAtTime 和 enqueueMessage 设置目标与时间。

源码文件:frameworks/base/core/java/android/os/Handler.java

相关函数:post()、sendMessageAtTime()、enqueueMessage()

java
public final boolean post(@NonNull Runnable r) {
    return sendMessageDelayed(getPostMessage(r), 0);
}

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

这里有三个容易混淆的时机:post() 返回 true 只代表消息进入队列;when 使用 SystemClock.uptimeMillis 时间基准,深度睡眠不会计入时间;Looper 退出后,尚未到期的消息可能被丢弃。因此“入队成功”不是“Runnable 一定执行”。

4. 队列等待 ​

Android 17 的源码树保留多个 MessageQueue 实现目录。本文按 LegacyMessageQueue 这条实现阅读:公共类负责 Java 链表和同步屏障,native 指针 mPtr 连接到 C++ NativeMessageQueue。

源码文件:frameworks/base/core/java/android/os/LegacyMessageQueue/MessageQueue.java

相关字段与函数:mPtr、mMessages、next()、enqueueMessage()

java
private long mPtr;
Message mMessages;
private boolean mQuitting;
private boolean mBlocked;

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
    mLooperThread = Thread.currentThread();
}

消息进入链表时,队列比较 when,决定插入头部、内部还是尾部;如果 Looper 正在阻塞且新消息成为队头,队列调用 nativeWake。这个判断的目的不是“每次入队都唤醒”,而是只在当前等待条件已经失效时唤醒线程。

源码文件:frameworks/base/core/java/android/os/LegacyMessageQueue/MessageQueue.java

相关函数:enqueueMessage()

java
boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null && !msg.isAsynchronous()) {
        throw new IllegalStateException("Message must have a target");
    }
    synchronized (this) {
        if (mQuitting) {
            msg.recycleUnchecked();
            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 {
            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;
        }
        if (needWake) nativeWake(mPtr);
    }
    return true;
}

队列中的 target == null 不是普通消息,而是同步屏障;它改变的是 next() 搜索可执行消息的规则。本文只用它解释唤醒判断,不把同步屏障的完整插入/移除机制混入这篇总览。

5. Native等待 ​

当 Java 队列没有立即可执行的消息时,next() 根据下一条消息的时间计算 timeout,然后调用 nativePollOnce(mPtr, timeoutMillis)。JNI 注册表把这个 Java native 方法绑定到 NativeMessageQueue::pollOnce,没有经过 Binder。

源码文件:frameworks/base/core/java/android/os/LegacyMessageQueue/MessageQueue.java

相关函数:next()

java
for (;;) {
    nativePollOnce(ptr, nextPollTimeoutMillis);
    synchronized (this) {
        final long now = SystemClock.uptimeMillis();
        Message msg = mMessages;
        if (msg != null && now >= msg.when) {
            mBlocked = false;
            mMessages = msg.next;
            msg.next = null;
            msg.markInUse();
            return msg;
        }
        if (pendingIdleHandlerCount <= 0) {
            mBlocked = true;
            continue;
        }
    }
}

源码文件:frameworks/base/core/jni/android_os_MessageQueue.cpp

相关函数:android_os_MessageQueue_nativePollOnce()、android_os_MessageQueue_nativeWake()

cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jlong ptr, jint timeoutMillis) {
    NativeMessageQueue* nativeMessageQueue =
            reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue =
            reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->wake();
}

JNI 层只做对象指针转换和方法转发;阻塞语义由 native Looper 决定。Java 队列中的时间排序和 C++ 中的 fd 事件是两套输入,pollOnce 返回后 Java 仍需重新检查消息链表。

源码文件:system/core/libutils/Looper.cpp

相关函数:Looper::Looper()、pollOnce()、pollInner()、wake()、awoken()

cpp
Looper::Looper(bool allowNonCallbacks)
      : mAllowNonCallbacks(allowNonCallbacks),
        mPolling(false),
        mNextMessageUptime(LLONG_MAX) {
    mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
    std::scoped_lock _l(mLock);
    rebuildEpollLocked();
}

int Looper::pollInner(int timeoutMillis) {
    int eventCount = epoll_wait(mEpollFd.get(), eventItems,
            EPOLL_MAX_EVENTS, timeoutMillis);
    if (eventCount == 0) return POLL_TIMEOUT;
    for (int i = 0; i < eventCount; i++) {
        if (eventItems[i].data.u64 == WAKE_EVENT_FD_SEQ) {
            awoken();
        }
    }
    return POLL_CALLBACK;
}

void Looper::wake() {
    uint64_t inc = 1;
    TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
}

eventfd 是唤醒通道,epoll 是等待集合。新消息成为队头时,Java nativeWake() 最终写入 eventfd;epoll_wait 返回后 awoken() 读取计数,消费唤醒事件。唤醒只保证线程重新检查 Java 队列,不直接携带 Message 对象。

6. 回调分发 ​

Java Looper.loop() 负责循环,不负责判断 Runnable 还是 Handler 子类。它从 MessageQueue.next() 获得消息,再调用 msg.target.dispatchMessage(msg);真正的三级分发在 Handler 中完成:优先 Message.callback,其次 Handler.Callback,最后 handleMessage。

源码文件:frameworks/base/core/java/android/os/Looper.java

相关函数:loop()、loopOnce()

java
public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called");
    }
    me.mInLoop = true;
    for (;;) {
        if (!loopOnce(me, ident, thresholdOverride)) return;
    }
}

msg.target.dispatchMessage(msg);
msg.recycleUnchecked();

源码文件:frameworks/base/core/java/android/os/Handler.java

相关函数:dispatchMessageImpl()

java
public void dispatchMessageImpl(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null && mCallback.handleMessage(msg)) {
            return;
        }
        handleMessage(msg);
    }
}

因此 post(r) 最终不会调用 handleMessage();它把 Runnable 放入 Message.callback,由第一分支执行。消息执行完成后,Looper 在 finally 路径回收 Message;如果 callback 抛出异常,异常会离开当前循环,进程是否继续由上层异常处理决定,不能把 post() 当作异常隔离机制。

7. 结束路径 ​

正常路径是 enqueue → next → dispatch → recycle。非正常路径至少有三种:Looper 已退出时,enqueueMessage() 回收消息并返回 false;消息到期前调用 quitSafely() 时,队列保留已到期消息并丢弃未来消息;callback 抛异常时,当前 loop() 不会自动吞掉异常。

源码文件:frameworks/base/core/java/android/os/LegacyMessageQueue/MessageQueue.java

相关函数:quit()、quitSafely()、removeAllMessagesLocked()

java
void quit(boolean safe) {
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }
    synchronized (this) {
        if (mQuitting) return;
        mQuitting = true;
        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            removeAllMessagesLocked();
        }
        nativeWake(mPtr);
    }
}

这解释了 Handler.post() 返回 false 的常见原因:绑定的 Looper 正在退出。调用者若需要保证任务完成,必须自己设计生命周期确认或取消协议;消息队列不会为已经返回 true 的消息提供完成回调。

8. 阅读实验 ​

在 Android 源码 checkout 中,可以用下面的命令把主线重新定位:

bash
rg -n "post\(|sendMessageAtTime|enqueueMessage|dispatchMessage" \
  frameworks/base/core/java/android/os/Handler.java
rg -n "prepare\(|loop\(|loopOnce|dispatchMessage" \
  frameworks/base/core/java/android/os/Looper.java
rg -n "nativePollOnce|nativeWake|enqueueMessage|Message next" \
  frameworks/base/core/java/android/os/LegacyMessageQueue/MessageQueue.java \
  frameworks/base/core/jni/android_os_MessageQueue.cpp
rg -n "eventfd|epoll_wait|void Looper::wake|void Looper::awoken" \
  system/core/libutils/Looper.cpp

可以做两个最小实验。第一,在 Handler.post() 前后记录线程名和 SystemClock.uptimeMillis(),确认 callback 在线程绑定的 Looper 上执行,而不是发送线程立即执行。第二,在线程调用 quitSafely() 后再次 post(),记录返回值并检查未来消息是否执行;这个实验只覆盖 Java 队列的退出语义,不代表 native fd 注册或所有 MessageQueue 变体都相同。

9. 边界 ​

本文追踪的是 Java Handler 消息投递到 native Looper 唤醒再返回 Java 分发的主线。没有展开同步屏障的完整 API、IdleHandler、HandlerThread、Binder 主线程调度、Choreographer 的 VSYNC 编排和 Combined/Deli MessageQueue 的差异;这些主题需要各自的源码入口,不能用本篇总览替代。