上篇文章我们从源码得角度讲述了Message和Looper,Message为消息的一个载体,而Looper可以说是消息处理机制的一个管家,这篇文章我将继续从源码的角度讲述消息处理机制中的MessageQueue和Handler。
3 MessageQueue
3.1入队操作
MessageQueue是存储消息的一个队列,Handler发送一个消息后该消息并不会立即被接收,而是存储在消息队列逐个被取出执行,在MessageQueue中将消息存储在队列是用过enqueueMessage()方法进行的,我们先来看一下它的源代码:
boolean enqueueMessage(Message msg, long when) {
//msg.target代表的是Message的宿主Handler,
//是在Message被Handler发送的时候进行赋值的
//判断Message的宿主Handler是否为空,为空直接抛出异常
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//是否处于in-use状态
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
//mQuitting为true代表当前MessageQueue已经退出
if (mQuitting) {
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;
}
//将消息置为in-use状态
msg.markInUse();
//设置消息被处理的时间,越小执行的顺序越靠前
msg.when = when;
//拿到队列的头部
Message p = mMessages;
//是否需要唤醒线程,最终是通过调用底层C++来实现的
boolean needWake;
//当队列头部为空 || 需要被执行的时间为0 ||小于队列头部的when
//当满足上述条件的一种就将消息放在队列的头部,让其最先执行
//需要注意的是,该队列是基于双向链表数据结构实现的
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//线程是否被阻塞 && 消息宿主Handler是非为null && 消息是否为异步消息
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//因为队列徐遵循先进新出FIFO规则,所以要先得到尾节点,
//将新元素插入到尾节点
for (;;) {
prev = p;
p = p.next;
//1.从头结点开始,循环得到下一个节点,
// 如果为null即到了尾节点随即跳出循环
//2.如果当前when小于某个节点的when,
// 即需要执行的顺序在该节点前面,也跳出循环
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//将当前节点插入到队尾或者根据when优先级插入到合适的位置
msg.next = p;
prev.next = msg;
}
if (needWake) {
//为native方法,用于唤醒线程
nativeWake(mPtr);
}
}
return true;
}
我再来带大家梳理一个整个插入的流程:
- 首先判断Message的宿主Handler是否为null以及是否处于in-use状态
- 再判断MessageQueue是否已经退出,是的话就将消息回收,返回false
- 将消息置为in-use状态,设置消息被处理的优先级,拿到队列头部
- 再判断队列是否为空、优先级是否最大,满足一个将消息插入队列头部
- 以上条件都不满足,将消息插入到队尾或者根据优先级插入到合适的位置
整个入队流程就是这样,跟普通队列唯一个区别就是里面多了一个when优先级
3.2 同步屏障
同步屏障:当设置了同步屏障之后会优先执行异步消息。简单说同步屏障也可以视为一种优先级的机制,异步消息优先于同步消息。在MessageQueue中可以通过postSyncBarrier()来添加异步消息,我们来看一下其源代码:
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
synchronized (this) {
//定义一个token,是每个异步消息的标识
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
//返回token
return token;
}
}
异步消息和同步消息入队步骤基本一致,只是多了一个token用来表示每一个异步消息。最后再提一点,通过该方法入队未设置target,所以target也就是宿主Hanlder为null。
- 注意:在SDK<23中postSyncBarrier位于Looper中,SDK>=23中postSyncBarrier位于MessageQueue中。
3.3 出队操作
在讲Looper的时候我们有了解到MessageQueue的出队操作调用的是next()方法,我们来看一下next()的源代码:
Message next() {
//一些native方法用到的属性
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);
synchronized (this) {
// 获取到当前的时间
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
//拿到头结点
Message msg = mMessages;
//此步骤为同步屏障
if (msg != null && msg.target == null) {
//如果当前消息为同步消息
//继续往下取,直到取出异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//如果还没到该消息的取出时间就阻塞至取出时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//将线程阻塞状态置为false
mBlocked = false;
//将该消息从链表中取出
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
//设置in-use状态
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
//剩余内容为底层控制线程的方法,想了解的可参考英文注释
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
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 MessageQueue.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 MessageQueue.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);
}
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;
}
}
消息出队步骤:
- 记性同步屏障操作,优先取出异步消息
- 当前时间还未到取出时间将当前线程阻塞至取出时间
- 进行出队操作
出队方法中包括很多Linux以及native内容,用来控制线程阻塞,因为牵扯到很多底层内容而这方面也是题注比较薄弱的地方,所以就不详细介绍了以免误人子弟了。
3.4 终止MessageQueue
在前面一篇文章中我们提到了Looper的停止操作,其实最终是通过调用MessageQueue的quit()方法实现的
源代码:
void quit(boolean safe) {
//
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
//已经退出直接return
if (mQuitting) {
return;
}
//将mQuitting置为退出状态
mQuitting = true;
//两种退出操作
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// 注销
nativeWake(mPtr);
}
}
//直接将所有消息出队
private void removeAllMessagesLocked() {
Message p = mMessages;
//将队列中的消息逐个取出并置为null,最后将其回收
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
//将还未被执行的消息出队
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
//如果头结点等待执行的时间大于当前时间,
//也就意味着头结点消息还未被执行,
//将所有消息进行出队操作
if (p.when > now) {
removeAllMessagesLocked();
} else {
//筛选出执行时间小于当前时间的消息,
//这部分消息代表正在被执行的消息,让其继续执行不进行销毁操作
Message n;
for (;;) {
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);
}
}
}
退出有两种方法分别是:
removeAllMessagesLocked():不管消息是否正在被执行,直接强制将所有的消息进行出队操作。
removeAllFutureMessagesLocked():首先筛选出未被执行的消息,将这类消息进行出队操作并回收,正在被执行的消息继续执行。
4 Handler
4.1 Handler的初始化
在Handler中有很多构造方法,但最终调用的只有两个,分别是 Handler(Handler.Callback callback, boolean async) 和Handler(Looper looper, Callback callback, boolean async),先看下源代码:
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
两种创建方式基本相同,第一种自己获取Lopper而第二种是通过构造函数进行传入。
4.2 发送消息
Handler类中发送消息的方法也有很多种,但最终调用的还是只有enqueueMessage()方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//将当前对象赋值给msg的target,用于后面接收消息
msg.target = this;
if (mAsynchronous) {
//设置消息为异步消息
msg.setAsynchronous(true);
}
//进行入队操作
return queue.enqueueMessage(msg, uptimeMillis);
}
Handler中发送消息代码也非常简单,只是将Message的target设置为当前Handler对象,然后判断是否设置消息为异步,最后将消息加入队列。
4.3 接收消息
我们在上面文章有讲到,在主线程中Looper取到消息后会执行一句代码:
//将消息交给Handler处理
msg.target.dispatchMessage(msg);
取出消息后交由Handler的dispatchMessage(msg)方法进行处理,我们来看下其源代码:
public void dispatchMessage(Message msg) {
//如果callback不为空,说明它是通过postXXX()发送的消息
if (msg.callback != null) {
handleCallback(msg);
} else {
//当Handler中设置了Callback,则执行Callback的handleMessage(msg)方法,如果返回true则结束方法
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//Handler自己处理
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
//这个方法就是我们在讲Handler使用中重写的那个方法
public void handleMessage(Message msg) {
}
Handler接收消息也很简单,首先判断callback 是否为null,不为null就执行Message中callback的run()方法,然后再判断Handler中是否设置了Callback对象,如果设置了执行Callback的handleMessage(msg)方法,返回true则结束方法,否则执行Hnandler自己的handleMessage()方法。
Handler中收发消息都是非常简单的,因为复杂的部分都在Looper和MessageQueue中,此外Handler中有很多发送消息的方法,这些方法的使用都非常的简单,文章中就不进行讲解,感兴趣的朋友可自行了解。
总结
消息处理机制可以说是每个Android开发者必掌握的内容,内部可分为四部分:Message、MessageQueue、Looper、Hndler,其中核心在Looper和MessageQueue中,掌握了这两个基本也就掌握了消息处理机制的原理。
在开写之前我认为一篇文章就能写完,但写到一半发现内容还是有点多的,所以就分了两篇文章。本篇文章到此结束,下篇文章我为大家带来《多线程系列(六)AsyncTask源码解读》