//MessageQueue.java
public final class MessageQueue {
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);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
//...
}
java层,在ActivityThread的main方法创建UI线程的消息循环,Looper.prepareMainLooper -> Looper.prepare -> new Looper -> new MessageQueue
//MessageQueue.java
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
//frameworks/base/core/jni/android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
//创建native消息队列NativeMessageQueue
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
//...
//增加引用计数
nativeMessageQueue->incStrong(env);
//使用C++强制类型转换符reinterpret_cast把NativeMessageQueue指针强转成long类型并返回到java层
return reinterpret_cast(nativeMessageQueue);
}
//frameworks/base/core/jni/android_os_MessageQueue.cpp
NativeMessageQueue::NativeMessageQueue()
: mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
//获取TLS中的Looper(Looper::getForThread相当于java层的Looper.mLooper中的ThreadLocal.get方法)
mLooper = Looper::getForThread();
if (mLooper == NULL) {
//创建native层的Looper
mLooper = new Looper(false);
//保存Looper到TLS中(Looper::setForThread相当于java层的ThreadLocal.set方法)
Looper::setForThread(mLooper);
}
}
//system/core/libutils/Looper.cpp
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
//1、构造唤醒事件的fd(文件描述符)
mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
//...
//2、重建epoll事件
rebuildEpollLocked();
}
文件描述符是什么?它就是一个int值,又叫做句柄,在Linux中,打开或新建一个文件,它会返回一个文件描述符,读写文件需要使用文件描述符来指定待读写的文件,所以文件描述符就是指代被打开的文件,所有对这个文件的IO操作都要通过文件描述符
但其实文件描述符也不仅仅是指代文件,它还有更多的含义,可以看后文的epoll机制解释。
//system/core/libutils/Looper.cpp
void Looper::rebuildEpollLocked() {
//1、关闭旧的管道
if (mEpollFd >= 0) {
close(mEpollFd);
}
//2、创建一个新的epoll文件描述符,并注册wake管道
mEpollFd = epoll_create(EPOLL_SIZE_HINT);//EPOLL_SIZE_HINT为8
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); //置空eventItem
//3、设置监听事件类型和需要监听的文件描述符
eventItem.events = EPOLLIN;//监听可读事件(EPOLLIN)
eventItem.data.fd = mWakeEventFd;//设置唤醒事件的fd(mWakeEventFd)
//4、将唤醒事件fd(mWakeEventFd)添加到epoll文件描述符(mEpollFd),并监听唤醒事件fd(mWakeEventFd)
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
//5、将各种事件,如键盘、鼠标等事件的fd添加到epoll文件描述符(mEpollFd),进行监听
for (size_t i = 0; i < mRequests.size(); i++) {
const Request& request = mRequests.valueAt(i);
struct epoll_event eventItem;
request.initEventItem(&eventItem);
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
request.fd, strerror(errno));
}
}
}
}
Looper构造函数涉及Linux epoll机制,epoll机制是Linux最高效的I/O复用机制
在native层通过epoll机制也建立了一套消息机制后,java层的消息循环也就创建好,在此之后就会在java层中启动消息循环,Looper.loop -> MessageQueue.next,在java层中每次循环去读消息时,都会调用MessageQueue的next函数,如下:
//MessageQueue.java
Message next() {
//...
for(;;){
nativePollOnce(ptr, nextPollTimeoutMillis);
//...
}
//...
}
//frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
//把ptr强转为NativeMessageQueue
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
//frameworks/base/core/jni/android_os_MessageQueue.cpp
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
//...
//核心是调用了native层的Looper的pollOnce方法
mLooper->pollOnce(timeoutMillis);
//...
}
//NativeMessageQueue是一个代理类,所以它把逻辑转交给Looper,
//system/core/libutils/Looper.h
inline int pollOnce(int timeoutMillis) {
//调用了带4个参数的pollOnce方法
return pollOnce(timeoutMillis, NULL, NULL, NULL);
}
//system/core/libutils/Looper.cpp
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
//一个死循环
for (;;) {
//...
//当result不等于0时,就会跳出循环,返回到java层
if (result != 0) {
//...
return result;
}
//处理内部轮询
result = pollInner(timeoutMillis);
}
}
pollInner方法返回一个int值result,代表着本次轮询是否成功处理了消息,当result不等于0时,就会跳出循环,返回到java层继续处理java层消息,result有以下4种取值:
enum {
//表示Looper的wake方法被调用,即管道的写端的write事件触发
POLL_WAKE = -1,
//表示某个被监听fd被触发。
POLL_CALLBACK = -2,
//表示等待超时
POLL_TIMEOUT = -3,
//表示等待期间发生错误
POLL_ERROR = -4,
};
//system/core/libutils/Looper.cpp
int Looper::pollInner(int timeoutMillis) {
//timeoutMillis等于-1,并且mNextMessageUptime不等于LLONG_MAX
//这说明java层没有消息但是native层有消息处理,这时在epoll_wait中,线程不能因为timeoutMillis等于-1而进入休眠,它还需要处理native层消息
//所以这里会根据mNextMessageUptime把timeoutMillis更新为大于0的值
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
if (messageTimeoutMillis >= 0
&& (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
//更新timeoutMillis为大于0的值,这个大于0的值就是需要等待多久后,才会到达native层消息的执行时间,等待timeoutMillis后,epoll_wait就会返回处理native层消息
timeoutMillis = messageTimeoutMillis;
}
//...
}
int result = POLL_WAKE;
//...
//事件集合(eventItems),EPOLL_MAX_EVENTS为最大事件数量,它的值为16
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
//1、等待事件发生或者超时(timeoutMillis),如果有事件发生,就从管道中读取事件放入事件集合(eventItems)返回,如果没有事件发生,进入休眠等待,如果timeoutMillis时间后还没有被唤醒,就会返回
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
//获取锁
mLock.lock();
//...省略的逻辑是:如果eventCount <= 0 都会直接跳转到Done:;标记的代码段
//2、遍历事件集合(eventItems),检测哪一个文件描述符发生了IO事件
for (int i = 0; i < eventCount; i++) {
//取出文件描述符
int fd = eventItems[i].data.fd;
//取出事件类型
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeEventFd) {//如果文件描述符为mWakeEventFd
if (epollEvents & EPOLLIN) {//并且事件类型为EPOLLIN(可读事件)
//这说明当前线程关联的管道的另外一端写入了新数据
//调用awoken方法不断的读取管道数据,直到清空管道
awoken();
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {//如果是其他文件描述符,就进行它们自己的处理逻辑
//...
}
}
//2、下面是处理Native的Message
Done:;
//mNextMessageUptime如果没有值,会被赋值成LLONG_MAX,但是如果mNextMessageUptime已经有值,它还是保持原来的值
mNextMessageUptime = LLONG_MAX;
//mMessageEnvelopes是一个Vector集合,它代表着native中的消息队列
while (mMessageEnvelopes.size() != 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
//取出MessageEnvelope,MessageEnvelop有收件人Hanlder和消息内容Message
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
//判断消息的执行时间
if (messageEnvelope.uptime <= now) {//消息到达执行时间
{
//获取native层的Handler
sp<MessageHandler> handler = messageEnvelope.handler;
//获取native层的消息
Message message = messageEnvelope.message;
mMessageEnvelopes.removeAt(0);
mSendingMessage = true;
//释放锁
mLock.unlock();
//通过MessageHandler的handleMessage方法处理native层的消息
handler->handleMessage(message);
}
mLock.lock();
mSendingMessage = false;
//result等于POLL_CALLBACK,表示某个监听事件被触发
result = POLL_CALLBACK;
} else {//消息还没到执行时间
//把消息的执行时间赋值给mNextMessageUptime
mNextMessageUptime = messageEnvelope.uptime;
//跳出循环,进入下一次轮询
break;
}
}
//释放锁
mLock.unlock();
//...
return result;
}
只要epoll_wait方法返回后,都会进入Done标记位的代码段, 就开始处理处理native层的Message, 在此之前先讲解一下MessageEnvelope,正如其名字,信封,其结构体定义在Looper.h中,如下:
//system/core/libutils/Looper.h
class Looper : public RefBase {
struct MessageEnvelope {
MessageEnvelope() : uptime(0) { }
MessageEnvelope(nsecs_t u, const sp<MessageHandler> h, const Message& m) : uptime(u), handler(h), message(m) {}
nsecs_t uptime;
//收信人handler
sp<MessageHandler> handler;
//信息内容message
Message message;
};
//...
}
MessageEnvelope里面记录着收信人(handler,MessageHandler类型,是一个消息处理类),发信时间(uptime),信件内容(message,Message类型),Message结构体,消息处理类MessageHandler都定义在Looper.h文件中, 在java层中,消息队列是一个链表,在native层中,消息队列是一个C++的Vector向量,Vector存放的是MessageEnvelope元素,接下来就进入一个while循环,里面会判断消息是否达到执行时间,如果到达执行时间,就会取出信封中的MessageHandler和Message,把Message交给MessageHandler的handlerMessage方法处理;如果没有到达执行时间,就会更新mNextMessageUptime为消息的执行时间,这样在下一次轮询时,如果由于java层没有消息导致timeoutMillis等于-1,就会根据mNextMessageUptime更新timeoutMillis为需要等待执行的时间,超时后返回继续处理native层消息队列的头部信息。
Java层通过Hanlder发送消息时,实际是把消息添加到消息队列,Handler.sendXX -> Handler.enqueueMessage -> MessageQueuue.enqueueMessage,最终会调用到MessageQueue的enqueueMessage方法,,如下:
//MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
//...
synchronized (this) {
//...
if (needWake) {
nativeWake(mPtr);
}
}
}
//frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
//NativeMessageQueue只是一个代理Looper的角色,该方法把操作转发给native层的Looper
//frameworks/base/core/jni/android_os_MessageQueue.cpp
void NativeMessageQueue::wake() {
mLooper->wake();
}
//system/core/libutils/Looper.cpp
void Looper::wake() {
uint64_t inc = 1;
//使用write函数通过mWakeEventFd往管道写入字符inc
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
//...
}
Looper的wake方法其实是使用write函数通过mWakeEventFd往管道写入字符inc,其中TEMP_FAILURE_RETRY 是一个宏定义, 当执行write方法失败后,会不断重复执行,直到执行成功为止,在nativeinit中,我们已经通过epoll_create方法监听了mWakeEventFd的可读事件,当mWakeEventFd可读时,epoll文件描述符就会监听到,这时epoll_wait方法就会从管道中读取事件返回,返回后就执行消息处理逻辑,所以这里的往管道写入字符inc,其实起到一个通知的作用,告诉监听的线程有消息插入了消息队列了,快点醒过来(因为进入了休眠状态)处理一下。
epoll_wait方法会让当前线程释放CPU资源进入休眠状态,等到下一个消息到达(mWakeEventFd会往管道写入字符)或监听的其他事件发生时就会唤醒线程,然后处理消息,