Android Handler机制——MessageQueue
一、NativeMessageQueue在java层的入口
public final class MessageQueue {
......
@UnsupportedAppUsage
@SuppressWarnings("unused")
private long mPtr; // used by native code
.....
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
@UnsupportedAppUsage
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
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);
}
mPtr是一个native层的指针,指向NativeMessageQueue的实例。下面六个方法为几个通过jni实现的native调用方法。
1).nativeInit:顾名思义,这是一个初始化函数,会初始化native层中相关对象,主要作用是实例化一个NativeMessageQueue对象,并将其指针返回到java层被mPtr成员变持有。该方法会在MessageQueue的构造函数中被调用。
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
2).nativeDestroy:取消java层中MessageQueue对native层中NativeMessageQueue对象的引用,主要实现方法是减少对NativeMessageQueue对象的强引用。
static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->decStrong(env);
}
该方法会在MessageQueue退出时被调用。
@Override
protected void finalize() throws Throwable {
try {
dispose();
} finally {
super.finalize();
}
}
// Disposes of the underlying message queue.
// Must only be called on the looper thread or the finalizer.
private void dispose() {
if (mPtr != 0) {
nativeDestroy(mPtr);
mPtr = 0;
}
}
void quit(boolean safe) {
......
mQuitting = true;
....
}
Message next() {
......
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
......
}
3).nativePollOnce:该方法的主要作用是阻塞线程。在native层中,Looper阻塞的主要原理是使用epoll进行阻塞,nativePollOnce最终会调用到epoll_wait方法进行阻塞,只有当epoll中注册的文件描述符触发了唤醒事件时线程才会被唤醒。该方法会在MessageQueue的next方法中被调用,而next又被Looper中的loop方法调用。
Message next() {
......
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
......
}
......
}
4).nativeWake:该方法的主要作用是唤醒线程。在native层中最终会调用到write函数向mWakeEventFd所对应的文件写一个字符,用于触发epoll。mWakeEventFd便是native层Looper默认使用的一个唤醒文件描述符,nativePollOnce和nativeWake两个方法最终操作的文件描述符都是它。该方法会被MessageQueue的enqueueMessage方法调用,而enqueueMessage则是在每次Handler发送message时都会被调用。
needWake是一个标志位,用于标记是否需要唤醒线程,主要由mBlocked成员变量决定。
boolean enqueueMessage(Message msg, long when) {
......
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
......
}
5).nativeIsPolling:用于检测epoll是否被阻塞。
6).nativeSetFileDescriptorEvents:设置epoll绑定的文件描述符。epoll可以绑定多个文件描述符,native层中Looper会默认绑定一个mWakeEventFd,但是它也支持用户绑定其他的文件描述符,该方法便是提供给用户用于绑定其他文件描述符的。一个典型应用便是InputChannel之间的通信(Inputflinger和客户端进行点击事件传输)。
二、构造
Message mMessages;
private final boolean mQuitAllowed;
private long mPtr; // used by native code
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
从MessageQueue的构造可以看出,其主要是两个参数。
mQuitAllowed的赋值可以追溯到Looper的初始化函数prepare()和prepareMainLooper(),当MessageQueue所负责的线程为主线程时,quitAllowed传入false,子线程传入true,表示MessageQueue是否可以退出。
mPtr是一个native层的指针,nativeInit()对应的C++层JNI方法如下,所在源码路径为:
frameworks/base/core/jni/android_os_MessageQueue.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);
}
可见,java层nativeInit()方法返回的是一个C++层指针,指向对象类型为NativeMessageQueue。NativeMessageQueue很关键,Handler机制能够实现阻塞、延时等功能依赖于MessageQueue,而MessageQueue实现这些功能则依赖于NativeMessageQueue,当然NativeMessageQueue底层是通过linux的epoll机制实现native层分析后续再写。
虽然MessageQueue的名字带有Queue,但其本质却是一个链表,当MessageQueue被塞入新的Message时,会对链表进行排序。成员变量mMessages是链表头,Message中也有一个成员变量next用于指向下一个节点的Message。
类关系图如下所示:
三、产物(Message)的出、入
MessageQueue作为生产者消费者模型中的产物仓库(缓冲区),其天然具备两个职责:1.产物放入;2.产物取出。代入Handler场景下,就是Message的存入和取出。MessageQueue只被Handler和Looper依赖,产物的存入、取出功能也分别被它们使用。
1.Message的放入
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) { //当MessageQueue已经退出时
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse(); //标上正在使用的标志
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) { //当链表为空时
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) { //插入节点的when时间小于链表中某个节点的when时间
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//插入链表
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
当mQuitting==true时,表示MessageQueue的quit方法已被调用,MessageQueue处于退出状态,不会再增加新的Message节点。当插入节点时,会比较Message中的when时间(该时间表示该Message需要被执行的时间),当被插入的Message的when时间小于相比较的节点when时间时,会将被插入的Message放到比较节点的前面,否则放到后面。是的,插入节点时便会根据when时间进行排序,when时间小的在前,大的在后。
该方法在Handler中调用sendMessage时被调用。
2.Message的取出
外界可以通过MessageQueue的next()获取Message,Looper的loop()方法中,便是通过queue.next()方法从MessageQueue中获取Message的。
Message msg = queue.next(); // might block
看注释可以知道,queue.next()可能会阻塞。是的,当MessageQueue中链表为空或者链表中第一个Message(即when时间最小的Message)的when时间大于当前时间时,next()方法便会阻塞住,知道有新的Message进入或者链表中的第一个Message的when时间大于等于当前时间。
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis); //调用native方法阻塞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) { //当Message没有绑定Handler时,放弃这个Message,一般不会出现这种情况
// 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) { //当前时间小于Message的when时间(message的指定执行时间)
// 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; //将message从链表中取出
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg; //返回message
}
} else {
// No more messages.
nextPollTimeoutMillis = -1; //为-1时nativePollOnce会无限阻塞下去
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) { //如果MessageQueue已经退出了,先调用dispose()销毁native资源,然后返回null
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}s
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
next()方法的主体是一个循环。进入循环首先会调用nativePollOnce这个native方法来阻塞nextPollTimeoutMillis这么长时间,而第一次进入循环时nextPollTimeoutMillis=0,不会阻塞。以上代码分析都有注释,可看注释。
三、停止
Handler机制的停止关键便是MessageQueue的停止,当用户调用Looper的quit时,Looper便会执行MessageQueue的quit方法。quit有一个传参safe,其决定是否执行完所有的非延时message。
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();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) { //链表里所有的Message的when时间都大于当前时间,则将链表中所有message都销毁
removeAllMessagesLocked();
} else {
Message n;
for (;;) { //取出链表里Message的when时间小于等于当前时间的message执行,然后将其他的message销毁
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked(); //将Message中的成员变量全部置0或置空
p = n;
}
mMessages = null;
}
protected void finalize() throws Throwable {
try {
dispose();
} finally {
super.finalize();
}
}
private void dispose() {
if (mPtr != 0) {
nativeDestroy(mPtr);
mPtr = 0;
}
}
需要注意的是,当销毁MessageQueue后,一定要调用dispose方法销毁native层资源。