Android应用程序的线程间通信(消息循环)是通过Handler,Looper和MessageQueue三者协同实现的.
Handler发送和处理消息,Looper创建消息队列和进入消息循环,MessageQueue描述消息队列.
消息循环涉及到Java层和native层,其中,Looper和MessageQueue在Java层和native层都有相应的类,Handler只在Java层有.Java层的Looper持有Java层MessageQueue的引用,在消息循环的时候Looper调用MessageQueue的next()方法获取需要处理的消息.Java层的MessageQueue又持有NativeMessageQueue的引用,NativeMessageQueue持有native层Looper的引用.native层的Looper有一个管道文件,管道文件的读端文件描述符和写端文件描述符,还有监听文件描述符I/O事件的epoll.当一个线程的消息队列中没有消息时,该线程就阻塞在管道的读端文件描述符上面,直到有其他线程通过这个管道的写端文件描述符来唤醒他.
相关类文件:
./frameworks/base/core/java/android/os/Handler.java
./frameworks/base/core/java/android/os/Looper.java
./system/core/include/utils/Looper.h
./system/core/libutils/Looper.cpp
./frameworks/base/core/java/android/os/MessageQueue.java
./frameworks/base/core/jni/android_os_MessageQueue.cpp
整体流程为Handler发送消息给MessageQueue,然后Looper从MessageQueue中将消息取出来交给Hander处理.
下面按照
1.消息队列创建
2.消息发送
3.消息循环和处理
三个步骤分析
1. 消息队列的创建
android应用的消息队列是用MessageQueue对象来描述的,他可以通过Looper的静态成员函数prepareMainLooper()或者prepare()方法创建.其中prepareMainLooper()为主线程创建消息队列,prepare()为子线程创建消息队列,但前者也是调用的后者.
下面以主线程消息队列的创建为例进行分析
应用进程创建后, 首先进入其主线程的ActivityThread.main()方法执行,在该方法中调用Looper.prepareMainLooper()方法创建主线程的消息队列.
./frameworks/base/core/java/android/os/Looper.java
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
首先调用prepare(false);
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实例,并存储在ThreadLocal中。也就是说每个线程都有一个Looper和MessageQueue.
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
./frameworks/base/core/java/android/os/MessageQueue.java
new一个MessageQueue实例
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
从MessageQueue的构造函数可以看出来,调用了native方法,并将返回值赋给成员变量mPtr.该变量是native层MessageQueue对象的地址,这样Java和native层的MessageQueue对象就能关联起来。
android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); //初始化native消息队列
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
创建了NativeMessageQueue实例
NativeMessageQueue::NativeMessageQueue() : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread(); //获取TLS中的Looper对象
if (mLooper == NULL) {
mLooper = new Looper(false); //创建native层的Looper
Looper::setForThread(mLooper); //保存native层的Looper到TLS中
}
}
创建了Looper实例并跟当前线程关联
./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) {
mWakeEventFd = eventfd(0, EFD_NONBLOCK);// 构建唤醒事件的fd
LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd. errno=%d", errno);
AutoMutex _l(mLock);
rebuildEpollLocked(); //重建epoll事件
}
void Looper::rebuildEpollLocked() {
if (mEpollFd >= 0) {
close(mEpollFd); //关闭旧的epoll实例
}
mEpollFd = epoll_create(EPOLL_SIZE_HINT); //创建新的epoll实例,并注册wake管道
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); //把未使用的数据区域进行置0操作
eventItem.events = EPOLLIN; //可读事件
eventItem.data.fd = mWakeEventFd;
//将唤醒事件(mWakeEventFd)添加到epoll实例(mEpollFd)
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
for (size_t i = 0; i < mRequests.size(); i++) {
const Request& request = mRequests.valueAt(i);
struct epoll_event eventItem;
request.initEventItem(&eventItem);
//将request队列的事件,分别添加到epoll实例
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, errno=%d", request.fd, errno);
}
}
}
用epoll机制监听管道的唤醒事件,有唤醒事件到来说明有其他线程向本线程发消息,没有唤醒事件,本线程就进入睡眠状态。
总结:
1) 调用prepare()方法创建Java层Looper
2) 在Java层Looper的构造函数里面创建Java层消息对象MessageQueue
3) 在Java层消息队列里面,通过jni创建native层消息队列nativeMessageQueue
4) 在nativeMessageQueue的构造函数里面,创建native层Looper
5) 在natvie层Looper里面创建管道文件的读端描述符,写端描述符和epoll实例,,然后用epoll监听管道读端描述符的唤醒事件.
2. 发送消息
在Hanlder中,主要有以下三个方法发送消息:
1)sendMessage(Message msg)
2)sendMessageDelayed(Message msg, long delayMillis)
3)sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
其中第一个是立刻发送,第二个是延迟多久发送,第三个是指定时间发送。不过,最终都会调用sendMessageAtTime()方法
然后进入MessageQueue.enqueueMessage(Message msg, long when)
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) { //消息队列为空,消息处理时间为0,和消息处理时间小于消息队列头部消息的处理时间,三种情况下将消息插入到消息头部。
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; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
消息队列中的消息是根据消息处理时间从小到大进行排列。
该方法首先根据参数when,将消息插入到合适的位置。然后判断是否需要唤醒线程。只有在消息插入到消息头部,且当前线程处于阻塞状态的时候才需要唤醒线程。mBlocked参数用来表示当前线程是否处于阻塞状态。
唤醒线程方法nativeWake(mPtr)最终调用到了native层Looper.wake()方法。
void Looper::wake() {
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
ALOGW("Could not write wake signal, errno=%d", errno);
}
}
}
可以看出唤醒线程就是向唤醒事件的管道里面写数据。数据是多少并不重要,写进去就行.
总结:
1)在Java层向消息队列里面添加消息
2)在native层向管道里面写数据,以便唤醒线程
3. 消息循环
./frameworks/base/core/java/android/os/Looper.java
从Looper.loop()开始循环:
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); // 获取需要处理的消息,可能会阻塞
...
msg.target.dispatchMessage(msg);//调用handler.dispatchMessage()进行分发处理
...
msg.recycleUnchecked();
}
}
然后进入MessageQueue.next()方法
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
...
}
}
在for循环里面,首先调用nativePollOnce()方法检查是否有新消息需要处理,如果有就从Message里面取出来并返回。如果没有,就检查线程中是否有注册空闲消息处理器,如果有空闲消息处理器,就发送空闲消息给空闲消息处理器进程处理,处理完后就进入睡眠状态,没有的话就直接进入睡眠状态。
现在分析native方法nativePollOnce()。该方法最终调用到Looper.cpp的pollinner()方法。
int Looper::pollInner(int timeoutMillis) {
...
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); //监听注册的文件描述符的IO读写事件,如果没有,则进入睡眠状态
...
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeEventFd) {
if (epollEvents & EPOLLIN) {
awoken(); //监听到有唤醒事件,也就是有其他向管道里面写数据,清除管道里面的数据,以便下一次通信。
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex >= 0) {
int events = 0;
if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
pushResponse(events, mRequests.valueAt(requestIndex));
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
"no longer registered.", epollEvents, fd);
}
}
}
...
return result;
}
在native方法返回后,MessageQueue就取出最新的消息并返回给Java层的Looper,Looper再调用Handler.dispatchMessage(msg)方法将消息分发给handler进行处理。
总结:
1) 在java层的Looper里面循环调用MessageQueue.next()方法获取消息,如果没有消息线程就阻塞在这里.
2)在native层利用epoll机制层监听管道是否有唤醒事件,如果有唤醒事件就通知Java层获取消息并处理.
注意:
1)native层应用的线程通信也是用的这套机制
2)空闲消息和空闲消息处理器机制,线程在进入睡眠之前会判断是否有注册空闲消息处理器,有的话就会发送空闲消息,不重要和不紧急的任务可以交给空闲消息处理器来处理。