android_os_MessageQueue.cpp
本文承接Native层总览,只研究一个 JNI 文件:android_os_MessageQueue.cpp。问题边界很窄,但调用关系很长:Java MessageQueue 的 mPtr 如何映射到 C++ 对象,nativePollOnce() 如何把 Java 对象暂时交给 native 回调,文件描述符事件如何回到 Java,以及异常为什么要延迟到 poll 返回后再抛出。
1. 文件角色
源码文件:frameworks/base/core/jni/android_os_MessageQueue.cpp
相关类型:NativeMessageQueue、MessageQueue、LooperCallback
class NativeMessageQueue : public MessageQueue, public LooperCallback {
public:
NativeMessageQueue();
virtual ~NativeMessageQueue();
virtual void raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj);
void pollOnce(JNIEnv* env, jobject obj, int timeoutMillis);
void wake();
void setFileDescriptorEvents(int fd, int events);
virtual int handleEvent(int fd, int events, void* data);
private:
JNIEnv* mPollEnv;
jobject mPollObj;
jthrowable mExceptionObj;
};这个类同时承担两种角色:它是 Java mPtr 指向的 native 对象,也是注册到 libutils::Looper 的 LooperCallback。mPollEnv、mPollObj 和 mExceptionObj 不是长期业务状态,而是一次 native poll 期间的 JNI 桥接状态。
2. 方法注册
源码文件:frameworks/base/core/jni/android_os_MessageQueue.cpp
相关函数:register_android_os_MessageQueue()
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},
{"nativeIsPolling", "(J)Z", (void*)android_os_MessageQueue_nativeIsPolling},
{"nativeSetFileDescriptorEvents", "(JII)V",
(void*)android_os_MessageQueue_nativeSetFileDescriptorEvents},
{"nativeSetSkipEpollWaitForZeroTimeout", "(J)V",
(void*)android_os_MessageQueue_nativeSetSkipEpollWaitForZeroTimeout},
};
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;
}注册表覆盖的不只是 poll/wake:nativeIsPolling() 给 Java 查询当前是否正在 native poll;nativeSetFileDescriptorEvents() 修改 FD 监听;nativeSetSkipEpollWaitForZeroTimeout() 修改 native Looper 的零超时策略。注册失败使用 RegisterMethodsOrDie,不会留下部分注册状态。
3. 指针句柄
源码文件:frameworks/base/core/jni/android_os_MessageQueue.cpp
相关函数:nativeInit()、nativeDestroy()、android_os_MessageQueue_getMessageQueue()
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);
}
static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue =
reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->decStrong(env);
}
sp<MessageQueue> android_os_MessageQueue_getMessageQueue(
JNIEnv* env, jobject messageQueueObj) {
jlong ptr = env->GetLongField(messageQueueObj, gMessageQueueClassInfo.mPtr);
return reinterpret_cast<NativeMessageQueue*>(ptr);
}nativeInit() 返回的是 C++ 地址编码后的 jlong,不是 Java 对象 ID。强引用在初始化时增加,在销毁时减少;Java MessageQueue.dispose() 负责把 mPtr 清零,避免同一个句柄重复销毁。getMessageQueue() 还被其他 native 代码用来从 Java 对象反查桥接对象。
4. Poll上下文
源码文件:frameworks/base/core/jni/android_os_MessageQueue.cpp
相关函数:nativePollOnce()、NativeMessageQueue::pollOnce()
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;
}
}mPollEnv 和 mPollObj 的有效范围严格包住 mLooper->pollOnce()。native Looper 只有在这段调用期间触发 handleEvent(),才能使用当前 JNI 环境和 Java MessageQueue 对象。poll 返回后两者立即清空,下一轮会重新设置。
5. 异常延迟
源码文件:frameworks/base/core/jni/android_os_MessageQueue.cpp
相关函数:MessageQueue::raiseAndClearException()、NativeMessageQueue::raiseException()
bool MessageQueue::raiseAndClearException(JNIEnv* env, const char* msg) {
if (env->ExceptionCheck()) {
jthrowable exceptionObj = env->ExceptionOccurred();
env->ExceptionClear();
raiseException(env, msg, exceptionObj);
env->DeleteLocalRef(exceptionObj);
return true;
}
return false;
}
void NativeMessageQueue::raiseException(JNIEnv* env, const char* msg,
jthrowable exceptionObj) {
if (exceptionObj) {
if (mPollEnv == env) {
if (mExceptionObj) {
env->DeleteLocalRef(mExceptionObj);
}
mExceptionObj = jthrowable(env->NewLocalRef(exceptionObj));
jniLogException(env, ANDROID_LOG_ERROR, LOG_TAG, exceptionObj);
} else {
jniLogException(env, ANDROID_LOG_ERROR, LOG_TAG, exceptionObj);
LOG_ALWAYS_FATAL("raiseException() was called when not in a callback, exiting.");
}
}
}JNI 回调如果留下 pending exception,raiseAndClearException() 先从 JNIEnv 取出并清掉,再交给 NativeMessageQueue 暂存。这样 native Looper 可以先结束当前回调和 poll;pollOnce() 返回后再用 env->Throw() 把异常还给 Java。若异常发生在没有匹配 mPollEnv 的上下文,代码选择记录并终止进程,而不是让异常悬挂。
6. 唤醒入口
源码文件:frameworks/base/core/jni/android_os_MessageQueue.cpp
相关函数:nativeWake()、NativeMessageQueue::wake()
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();
}JNI 层不保存 Java Message,也不判断队列是否需要唤醒;它只把句柄转换后转发给 native Looper。是否真的需要写 eventfd,由 Java MessageQueue.enqueueMessage() 的 mBlocked/needWake 分支决定。
7. FD掩码
源码文件:frameworks/base/core/jni/android_os_MessageQueue.cpp
相关函数:setFileDescriptorEvents()、handleEvent()
static const int CALLBACK_EVENT_INPUT = 1 << 0;
static const int CALLBACK_EVENT_OUTPUT = 1 << 1;
static const int CALLBACK_EVENT_ERROR = 1 << 2;
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);
}
}Java 的输入/输出/错误掩码和 libutils EVENT_INPUT/OUTPUT 不是同一组常量,JNI 在这里逐位转换。错误、挂断和无效 FD 由 handleEvent() 合并为 Java 的 CALLBACK_EVENT_ERROR,而不是让 Java 直接读取 epoll 原始标志。
8. 反向回调
源码文件:frameworks/base/core/jni/android_os_MessageQueue.cpp
相关函数:NativeMessageQueue::handleEvent()、WeakLooperCallback::handleEvent()
int NativeMessageQueue::handleEvent(int fd, int looperEvents, void* data) {
int events = 0;
if (looperEvents & Looper::EVENT_INPUT) events |= CALLBACK_EVENT_INPUT;
if (looperEvents & Looper::EVENT_OUTPUT) events |= CALLBACK_EVENT_OUTPUT;
if (looperEvents & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP
| Looper::EVENT_INVALID)) events |= CALLBACK_EVENT_ERROR;
int oldWatchedEvents = reinterpret_cast<intptr_t>(data);
int newWatchedEvents = mPollEnv->CallIntMethod(
mPollObj, gMessageQueueClassInfo.dispatchEvents, fd, events);
if (!newWatchedEvents) return 0;
if (newWatchedEvents != oldWatchedEvents) {
setFileDescriptorEvents(fd, newWatchedEvents);
}
return 1;
}
int NativeMessageQueue::WeakLooperCallback::handleEvent(
int fd, int events, void* data) {
sp<LooperCallback> callback = mCallback.promote();
if (callback != nullptr) return callback->handleEvent(fd, events, data);
return 0;
}弱回调代理避免 native Looper 无条件延长 NativeMessageQueue 的回调生命周期。目标对象已经消失时返回 0,让 native Looper 注销 FD;对象仍在时才进入 Java dispatchEvents()。Java 返回的掩码变化会再次配置 native 监听。
9. Java消费者
源码文件:frameworks/base/core/java/android/os/LegacyMessageQueue/MessageQueue.java
相关函数:dispatchEvents()
private int dispatchEvents(int fd, int events) {
final FileDescriptorRecord record;
final int oldWatchedEvents;
final OnFileDescriptorEventListener listener;
final int seq;
synchronized (this) {
record = mFileDescriptorRecords.get(fd);
if (record == null) return 0;
oldWatchedEvents = record.mEvents;
events &= oldWatchedEvents;
if (events == 0) return oldWatchedEvents;
listener = record.mListener;
seq = record.mSeq;
}
int newWatchedEvents = listener.onFileDescriptorEvents(
record.mDescriptor, events);
if (newWatchedEvents != 0) {
newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR;
}
return newWatchedEvents;
}Java 先在锁内快照 listener 和序号,再锁外调用用户 listener,避免持锁执行应用代码。listener 返回 0 会让 JNI 返回 0,native Looper 注销;返回非零则自动补上 error 监听。这个返回值是 FD 监听生命周期协议,不是 MessageQueue 的消息处理返回值。
10. 测试输入
源码文件:frameworks/base/core/tests/coretests/src/android/os/MessageQueueTest.java
相关函数:testResetClearsFileDescriptorEventListeners()
queue.addOnFileDescriptorEventListener(
reader.getFD(), OnFileDescriptorEventListener.EVENT_INPUT, readerCallback);
writer.write(0);
writer.flush();
syncWait(handler);
assertEquals(1, fdEventLatch.getCount());
resetQueue();
writer.write(0);
writer.flush();
syncWait(handler);
assertEquals(1, fdEventLatch.getCount());测试先向 pipe 写入数据,断言 FD listener 被触发;重置队列后再次写入,断言旧 listener 不再收到事件。它覆盖“注册 → native epoll → Java 回调 → reset 清理”的路径,不证明所有错误/挂断事件或并发 FD 关闭竞态。
11. 生命周期
源码文件:frameworks/base/core/jni/android_os_MessageQueue.cpp
相关函数:nativeDestroy()
static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue =
reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->decStrong(env);
}销毁只减少 NativeMessageQueue 的强引用;Java dispose() 随后把 mPtr 设为 0。FD 记录通常由 Java MessageQueue 的移除或 resetForTest() 先清理;不能只依赖 native 对象析构来推断 Java listener 已经停止执行。
12. 动手验证
rg -n "nativeInit|nativeDestroy|nativePollOnce|nativeWake|nativeSetFileDescriptorEvents" \
frameworks/base/core/jni/android_os_MessageQueue.cpp
rg -n "dispatchEvents|addOnFileDescriptorEventListener|removeOnFileDescriptorEventListener" \
frameworks/base/core/java/android/os/LegacyMessageQueue/MessageQueue.java
rg -n "testResetClearsFileDescriptorEventListeners" \
frameworks/base/core/tests/coretests/src/android/os/MessageQueueTest.java阅读顺序建议是:先看注册表确认签名,再跟 mPtr 进入对象,接着看 pollOnce() 的上下文窗口,最后沿 FD 测试检查 listener 的返回值如何影响注销。不要把 JNI 方法名本身当作行为,行为要继续追到 Java 或 Looper 消费者。
13. 边界说明
本文不展开 libutils::Looper 的完整 epoll 重建、native 消息 envelope、JNI 性能成本或所有 JNIEnv 生命周期规则。固定源码能支持的是本文件中的注册、句柄、异常、掩码和回调路径;具体 FD 是否可读仍由内核事件和 Java listener 决定。
