MessageQueue即消息队列,这个消息队列和上篇文章里面的Android Handler机制5之Message简介与消息对象对象池里面的 消息对象池 可不是同一个东西。MessageQueue是一个消息队列,Handler将Message发送到消息队列中,消息队列会按照一定的规则取出要执行的Message。需要注意的是Java层的MessageQueue负责处理Java的消息,native也有一个MessageQueue负责处理native的消息,本文重点是Java层,所以暂时不分析native源码。
Low-level class holding the list of messages to be dispatched by a Looper. Messages are not added directly to a MessageQueue, but rather through Handler objects associated with the Looper. You can retrieve the MessageQueue for the current thread with Looper.myQueue().
翻译一下:
它是一个被Looper分发、低等级的持有Message集合的类。Message并不是直接加到MessageQueue的,而是通过Handler对象和Looper关联到一起。 我们可以通过Looper.myQueue()方法来检索当前线程的MessageQueue 它是一个低等级的持有Messages集合的类,被Looper分发。Messages并不是直接加到MessageQueue的,而是通过Handler对象和Looper关联到一起。我们可以通过Looper.myQueue()方法来检索当前线程的
// True if the message queue can be quit.
//用于标示消息队列是否可以被关闭,主线程的消息队列不可关闭
private final boolean mQuitAllowed;
@SuppressWarnings("unused")
// 该变量用于保存native代码中的MessageQueue的指针
private long mPtr; // used by native code
//在MessageQueue中,所有的Message是以链表的形式组织在一起的,该变量保存了链表的第一个元素,也可以说它就是链表的本身
Message mMessages;
//当Handler线程处于空闲状态的时候(MessageQueue没有其他Message时),可以利用它来处理一些事物,该变量就是用于保存这些空闲时候要处理的事务
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
// 注册FileDescriptor以及感兴趣的Events,例如文件输入、输出和错误,设置回调函数,最后
// 调用nativeSetFileDescriptorEvent注册到C++层中,
// 当产生相应事件时,由C++层调用Java的DispathEvents,激活相应的回调函数
private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
// 用于保存将要被执行的IdleHandler
private IdleHandler[] mPendingIdleHandlers;
//标示MessageQueue是否正在关闭。
private boolean mQuitting;
// Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
// 标示 MessageQueue是否阻塞
private boolean mBlocked;
// The next barrier token.
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
// 在MessageQueue里面有一个概念叫做障栅,它用于拦截同步的Message,阻止这些消息被执行,
// 只有异步Message才会放行。障栅本身也是一个Message,只是它的target为null并且arg1用于区分不同的障栅,
// 所以该变量就是用于不断累加生成不同的障栅。
private int mNextBarrierToken;
通过分析下图
MessageQueue构造函数.png
我们知道MessageQueue就一个构造函数
代码在MessageQueue.java 68行
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
MessageQueue只是有一个构造函数,该构造函数是包内可见的,其内部就两行代码,分别是设置了MessageQueue是否可以退出和native层代码的相关初始化
在MessageQueue的构造函数里面调用 nativeInit(),我们来看下
代码在MessageQueue.java 61行
private native static long nativeInit();
根据Android跨进程通信IPC之3——关于"JNI"的那些事中知道,nativeInit这个native方法对应的是android_os_MessageQueue.cpp里面的android_os_MessageQueue_nativeInit(JNIEnv* , jclass )函数
代码在android_os_MessageQueue.cpp 172 行
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
// 初始化native消息队列
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
后面在讲解流程时候的会详细讲解,这里就不如深入了。
作为Android开发者我们知道,Handler除了用于发送Message,其本身也承载着执行具体业务逻辑的责任handlerMessage(Message msg),而IdleHandler在处理业务逻辑方面和Handler一样,不过它只会在线程空闲的时候才执行业务逻辑的处理,这些业务经常是哪些不是很紧要或者不可预期的,比如GC。
代码在MessageQueue.java 777行
/**
* Callback interface for discovering when a thread is going to block
* waiting for more messages.
*/
public static interface IdleHandler {
/**
* Called when the message queue has run out of messages and will now
* wait for more. Return true to keep your idle handler active, false
* to have it removed. This may be called if there are still messages
* pending in the queue, but they are all scheduled to be dispatched
* after the current time.
*/
boolean queueIdle();
}
老规矩 先来翻译一下接口的注释:
回调的接口,当线程空闲的时候可以利用它来处理一些业务员
这个IdleHandler接口就一个抽象方法queueIdle,我也看一下抽象方法的注释
当消息队内所有的Message都执行完之后,该方法会被调用。该返回值为True的时候,IdleHandler会一直保持在消息队列中,False则会执行完该方法后移除IdleHandler。需要注意的是,当消息队列中还有其他Delay Message并且这些Message还没到被执行的时间的时候,由于线程是空闲的,所以IdleHandler也可能会被执行,
从源码可以看出IdleHandler其实就是一个简单的回调接口,内部就一个带返回值的方法boolean queueIdle(),在使用的时候只需要实现该接口并加入到MessageQueue中就可以了,例如
从源码可以看出IdleHandler其实就是一个简单的回调接口,内部就一个带返回值的方法boolean queueIdle(),在使用的时候只需要实现该接口并加入到MessageQueue中就可以了,例如下面简答的代码所示
MessageQueue messageQueue = Looper.myQueue();
messageQueue.addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
// do something.
return false;
}
});
代码在MessageQueue.java 115行
/**
* Add a new {@link IdleHandler} to this message queue. This may be
* removed automatically for you by returning false from
* {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
* invoked, or explicitly removing it with {@link #removeIdleHandler}.
*
* <p>This method is safe to call from any thread.
*
* @param handler The IdleHandler to be added.
*/
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
看先注释:
方法内部很简单,就是三步
133行
/**
* Remove an {@link IdleHandler} from the queue that was previously added
* with {@link #addIdleHandler}. If the given object is not currently
* in the idle list, nothing is done.
*
* <p>This method is safe to call from any thread.
*
* @param handler The IdleHandler to be removed.
*/
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
看先注释:
移除IdleHandler的方法同样很简单,下一步同步处理然后直接mIdleHandlers.reomve(handler)就可以了。
在MessageQueue中,Message被分成3类,分别是
那我们就一次来看下:
正常情况下我们通过Handler发送的Message都属于同步消息,除非我们在发送的时候执行该消息是一个异步消息。同步消息会按顺序排列在队列中,除非指定Message的执行时间,否咋Message会按顺序执行。
想要往消息队列中发送异步消息,我们必须在初始化Handler的时候通过构造函数public Handler(boolean async)中指定Handler是异步的,这样Handler在讲Message加入消息队列的时候就会将Message设置为异步的。
障栅(Barrier) 是一种特殊的Message,它的target为null(只有障栅的target可以为null,如果我们自己视图设置Message的target为null的话会报异常),并且arg1属性被用作障栅的标识符来区别不同的障栅。障栅的作用是用于拦截队列中同步消息,放行异步消息。就好像交警一样,在道路拥挤的时候会决定哪些车辆可以先通过,这些可以通过的车辆就是异步消息。
同步和异步.png
458行
/**
* Posts a synchronization barrier to the Looper's message queue.
*
* Message processing occurs as usual until the message queue encounters the
* synchronization barrier that has been posted. When the barrier is encountered,
* later synchronous messages in the queue are stalled (prevented from being executed)
* until the barrier is released by calling {@link #removeSyncBarrier} and specifying
* the token that identifies the synchronization barrier.
*
* This method is used to immediately postpone execution of all subsequently posted
* synchronous messages until a condition is met that releases the barrier.
* Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
* and continue to be processed as usual.
*
* This call must be always matched by a call to {@link #removeSyncBarrier} with
* the same token to ensure that the message queue resumes normal operation.
* Otherwise the application will probably hang!
*
* @return A token that uniquely identifies the barrier. This token must be
* passed to {@link #removeSyncBarrier} to release the barrier.
*
* @hide
*/
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
看先注释:
方法内部很简单就是调用了postSyncBarrier(SystemClock.uptimeMillis()),通过Android Handler机制3之SystemClock类,我们知道SystemClock.uptimeMillis()是手机开机到现在的时间。那我们来看下这个postSyncBarrier(long)方法
462行
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
/** 第一步 */
final int token = mNextBarrierToken++;
/** 第二步 */
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
/** 第三步 */
Message prev = null;
//把消息队列的第一个元素指向p
Message p = mMessages;
if (when != 0) {
/** 第四步 */
while (p != null && p.when <= when) {
//通过p的时间点和障栅的时间点的比较,如果比障栅的小,就把消息队列中的消息向后移动一位(因为消息队列中所有元素是按照时间排序的)
prev = p;
p = p.next;
}
}
/** 第五步 */
//prev != null 代表不是消息队列的头部,则需要考虑前面一个消息和后面的一个消息
if (prev != null) { // invariant: p == prev.next
//msg的下一个消息是p
msg.next = p;
//msg的上一个消息是msg
prev.next = msg;
} else {
//prev == null 代表是消息队列的头部,则只需要负责下一个消息即可
msg.next = p;
//设置自己是消息队列的头部
mMessages = msg;
}
/** 第六步 */
return token;
}
}
方法详解
从源码中我们可以看出,在把障栅插入队列的时候先通过when的比较,根据不同的情况把障栅插入到不同的位置,具体情况如下图所示:
ps:蓝色的为Message、红色的为Barrier
当Message.when<Barrier.when,也就是第一个Message的执行时间点在障栅之前。
障栅插入队列1.png
当Message.when>=Barrier.when,也就是第一个Message的执行时间点在障栅之后。
障栅插入队列2.png
501行
/**
* Removes a synchronization barrier.
*
* @param token The synchronization barrier token that was returned by
* {@link #postSyncBarrier}.
*
* @throws IllegalStateException if the barrier was not found.
*
* @hide
*/
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
// 获取消息队列的第一个元素
Message p = mMessages;
//遍历消息队列的所有元素,直到p.targe==null并且 p.arg1==token才是我们想要的障栅
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
//是否需要唤醒
final boolean needWake;
//如果是障栅是不是第一个圆度
if (prev != null) {
//跳过障栅,将障栅的上一个元素的next指向障栅的next
prev.next = p.next;
//因为有元素,所以不需要唤醒
needWake = false;
} else {
//如果是第一个元素,则直接下消息队列中的第一个元素指向障栅的下一个即可
mMessages = p.next;
//如果消息队列中的第一个元素是null则说明消息队列中消息,所以需要唤醒
//
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
删除障栅(barrier)的方法也很简单,就是不断地遍历消息队列(链表结构),直到倒找与指定的token相匹配的障栅,然后把它从队列中移除。