Skip to content

MessageQueue初始化与nativeInit

从 MessageQueue 构造追踪 nativeInit、NativeMessageQueue、线程 Looper 和销毁边界。

基于android-17.0.0_r1
AndroidMessageQueueJNINative Looper源码阅读

MessageQueue初始化与nativeInit ​

本文承接Looper线程绑定和Looper主循环。前文把 MessageQueue.next() 当作等待入口;这里向下追踪这条队列在构造时如何取得 native 指针,以及这个指针怎样连接到 native Looper。重点不是泛讲 JNI,而是回答:Java 对象、JNI long mPtr、NativeMessageQueue 和线程级 native Looper 分别拥有哪个状态,初始化成功后谁消费它,销毁时谁释放它。

1. Java构造 ​

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

相关字段:mQuitAllowed、mPtr、mLooperThread

java
private final boolean mQuitAllowed;
private long mPtr; // used by native code
private final Thread mLooperThread;

private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);

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

构造顺序很具体:先保存 Java 层退出权限,再让 JNI 创建 native 对象,最后记录创建线程。mQuitAllowed 不会传给 nativeInit();它只由 Java 队列的 quit(boolean) 消费。相反,mPtr 不参与 Java 链表排序,而是交给 nativePollOnce()、nativeWake() 和 nativeDestroy()。

2. JNI注册 ​

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

相关函数:register_android_os_MessageQueue()

cpp
static const JNINativeMethod gMessageQueueMethods[] = {
        {"nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit},
        {"nativeDestroy", "(J)V", (void*)android_os_MessageQueue_nativeDestroy},
        {"nativePollOnce", "(JI)V", (void*)android_os_MessageQueue_nativePollOnce},
        {"nativeWake", "(J)V", (void*)android_os_MessageQueue_nativeWake},
};

int register_android_os_MessageQueue(JNIEnv* env) {
    int res = RegisterMethodsOrDie(env, "android/os/MessageQueue",
            gMessageQueueMethods, NELEM(gMessageQueueMethods));
    jclass clazz = FindClassOrDie(env, "android/os/MessageQueue");
    gMessageQueueClassInfo.mPtr = GetFieldIDOrDie(env, clazz, "mPtr", "J");
    gMessageQueueClassInfo.dispatchEvents = GetMethodIDOrDie(
            env, clazz, "dispatchEvents", "(II)I");
    return res;
}

注册表把 Java 方法签名和 C++ 函数地址配对,并缓存 mPtr 字段与 dispatchEvents 方法 ID。这里的 J 是 JNI long,对应 Java 侧保存 native 地址的 long;(JI)V 表示 native poll 接收指针和超时并返回 void。方法注册失败使用 RegisterMethodsOrDie,不会留下一个“部分可用”的队列。

3. native初始化 ​

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

相关函数:android_os_MessageQueue_nativeInit()、NativeMessageQueue::NativeMessageQueue()

cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    if (!nativeMessageQueue) {
        jniThrowRuntimeException(env, "Unable to allocate native queue");
        return 0;
    }

    nativeMessageQueue->incStrong(env);
    return reinterpret_cast<jlong>(nativeMessageQueue);
}

NativeMessageQueue::NativeMessageQueue()
        : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}

native 初始化先分配 NativeMessageQueue,构造函数再查当前线程的 native Looper。如果线程已经有 native Looper,就复用它;否则创建一个 new Looper(false) 并通过 Looper::setForThread() 登记。这里的 native 线程绑定与 Java 的 sThreadLocal<Looper> 是两套存储,不能把一个字段的值当作另一个字段的证明。

4. 指针所有权 ​

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

相关函数:nativeInit()、nativeDestroy()

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

nativeInit() 创建对象后增加强引用,再把地址交给 Java mPtr;nativeDestroy() 把同一地址转回对象并减少强引用。Java 的 dispose() 在调用后把 mPtr 设为 0,避免同一个 Java 队列重复销毁同一指针:

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

相关函数:dispose()

java
private void dispose() {
    if (mPtr != 0) {
        nativeDestroy(mPtr);
        mPtr = 0;
    }
}

正常退出时 next() 调用 dispose();如果 Java 队列没有走退出路径,finalize() 仍会尝试清理。源码注释限制 dispose() 只能在 Looper 线程或 finalizer 中执行,原因是 native poll 和 Java 回调可能正在使用同一 native 对象。

5. 等待桥接 ​

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

相关函数:android_os_MessageQueue_nativePollOnce()、NativeMessageQueue::pollOnce()

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

void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    mPollEnv = env;
    mPollObj = pollObj;
    mLooper->pollOnce(timeoutMillis);
    mPollObj = NULL;
    mPollEnv = NULL;

    if (mExceptionObj) {
        env->Throw(mExceptionObj);
        env->DeleteLocalRef(mExceptionObj);
        mExceptionObj = NULL;
    }
}

Java next() 传入超时,JNI 将它交给当前线程的 native Looper。poll 返回后先清除回调上下文指针,再检查 native 回调期间缓存的 Java 异常,最后重新抛回 Java。于是 nativePollOnce() 不只是“阻塞一下”:它还负责把 native 回调异常带回 Java 调用栈。

6. 唤醒桥接 ​

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

相关函数:android_os_MessageQueue_nativeWake()、NativeMessageQueue::wake()

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

void NativeMessageQueue::wake() {
    mLooper->wake();
}

Java 入队或退出时调用 nativeWake(),JNI 不创建新的线程,也不直接改 Java 链表;它只把唤醒请求交给同一个 native Looper。nativePollOnce() 被唤醒后仍要回到 Java MessageQueue.next() 重新检查消息和 mQuitting,因此唤醒本身不等于消息已经取出。

7. 文件描述符 ​

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

相关函数:setFileDescriptorEvents()、handleEvent()

cpp
void NativeMessageQueue::setFileDescriptorEvents(int fd, int events) {
    if (events) {
        int looperEvents = 0;
        if (events & CALLBACK_EVENT_INPUT) {
            looperEvents |= Looper::EVENT_INPUT;
        }
        if (events & CALLBACK_EVENT_OUTPUT) {
            looperEvents |= Looper::EVENT_OUTPUT;
        }
        mLooper->addFd(fd, Looper::POLL_CALLBACK, looperEvents,
                sp<WeakLooperCallback>::make(this),
                reinterpret_cast<void*>(events));
    } else {
        mLooper->removeFd(fd);
    }
}

MessageQueue 的 native 对象还承担 Java 文件描述符监听与 native Looper 的适配。监听事件改变时由 addFd/removeFd 更新 native Looper;事件发生后 handleEvent() 再通过缓存的 mPollEnv、mPollObj 调用 Java dispatchEvents。这条能力说明 mPtr 的消费者不只有消息超时等待。

8. 初始化失败 ​

初始化失败有明确边界:JNI 注册阶段找不到类、字段或方法会通过 RegisterMethodsOrDie、FindClassOrDie 或 GetFieldIDOrDie 终止注册;运行时 new NativeMessageQueue() 失败则设置 Java RuntimeException 并返回 0。正常构造路径没有把 0 当作可用指针继续使用,后续 next() 看到 mPtr == 0 会返回 null。

需要区分两种“没有 Looper”:Java Looper.prepare() 负责 Java ThreadLocal,native 构造函数负责 native Looper::getForThread()。前者缺失会让 Looper.loop() 抛异常,后者缺失则由 NativeMessageQueue 创建并登记 native Looper;它们不是同一个失败分支。

9. 动手验证 ​

在 AOSP 源码树定位 Java/JNI 配对:

bash
rg -n "nativeInit|nativeDestroy|nativePollOnce|nativeWake" \
  frameworks/base/core/java/android/os/LegacyMessageQueue/MessageQueue.java \
  frameworks/base/core/jni/android_os_MessageQueue.cpp
rg -n "RegisterMethodsOrDie|mPtr|dispatchEvents" \
  frameworks/base/core/jni/android_os_MessageQueue.cpp

再沿着 Looper.loopOnce() → MessageQueue.next() → nativePollOnce() 阅读,不要从 NativeMessageQueue::pollOnce() 单独推断 Java 消息排序;排序、屏障和退出标志仍由 Java MessageQueue 保存和判断。

10. 边界说明 ​

本文只说明 JNI 桥接和对象生命周期,不展开 libutils/Looper 的 epoll、eventfd 实现,也不把 NativeMessageQueue 的 native Looper 绑定等同于 Java sThreadLocal。mPtr 是一个生命周期句柄,不是 Java Message 链表,也不是可供业务代码直接解释的对象地址。