需要搞明白Handler的流程,就是从sendMessage一直到handleMessage的过程。
首先来看sendMessage,从图中就可以看出来,它就是把货物放到了这个传送带上来。
在Handler.java这个类里面,有很多函数用来发送消息。其中send开头的就有这些:
然后还有post开头的:
也就是说在这个类里面,就有这么多的函数用来发送消息的。它们之间有什么关系呢?
看这个图,也就是说任意一个send,无论它的流程怎么跑,最后它都会调度在了enqueueMessgae这个方法中。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
而Handler.enqueueMessage()又调用了MessageQueue.enqueueMessage(),所以,这一系列的函数,最终的目的都是在消息队列中添加了消息。
在enqueueMessage()中,它维持了一个消息队列,这个消息队列其实是一个优先级队列。
优先级队列
假如有一个M4要插入到这个队列中去,它首先要轮训M1-M2-M3这个队列;
然后分别比较M4与M1、M2、M3...;
看到M4刚好比M2大,且比M3小,就插入到这个队列中去,这个队列也就是个有序的队列
在源码里面,MessageQueue.java中的enqueueMessage方法里面有一个for循环,他的作用就是根据时间when进行一个排序。
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
看这个for循环,就是对单链表的一个轮询,其关键点就在于找到循环中的break。
enqueueMessage(Message msg, long when)
when是传入的值,代表这个消息什么时候被执行,如果when等于0,那就是立刻执行。
首先,它会拿到第一个节点p。
p = p.next;
将第一个节点的时刻p.when,与我们刚加入的这个消息的时刻when对比。
when < p.when
如果新加入的消息的时刻小于前一个节点的时刻,那么新加入的消息就要比前一个节点的消息先执行,那么,新加入的消息就要插入到这个消息的之前,所以就执行break停止查找,然后就将消息插入到前面。
msg.next = p;
它沿着这个序列进行轮询,先查询第一个消息的执行时间,如果第一个消息的执行时间早于新插入的这个消息的节点,再去查找第二个节点,这时候发现第二个消息的发送的时间又比这个时间早,他就会再去查找第三个,他发现第三个消息的执行时间要比插入的消息的执行时间晚,这时候,就可以把这个消息插入到了这个消息的队列中来,这样就形成了一个按照时间先后进行排序的有序队列。
但我们添加的消息基本上是不相同的,除非是0。
因为在sendMessageDelayed中,它的延迟时间是系统时间加上delay的时间,而系统时间是每时每刻都在增大的,就像是for循环每走一遍,他的时间也会增大。
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
而如果等于0,则直接插入。
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
}
前面说到往MessageQueue这个消息队列中存消息,就一定会有从这个消息队列中取消息,next()函数就是返回了一个Message。
Message next(){
....
}
next()函数是如何取的消息,要先看下这个if语句。
//开机到现在的毫秒数如果小于msg.when则代表还未到发送消息的时间
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
// 虽然有消息,但是还没有到运行的时候
//计算还有等待多久,并赋值给nextPollTimeoutMillis
//设置下一次查询消息需要等待的时长
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
//代表当前没有阻塞
mBlocked = false;
// 获取msg并且删除该节点
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;
}
他先用 当前时刻now 和 它所拿到的队头的时刻msg.when 进行对比,now < msg.when 也就是说,消息队列的第一个节点还没到可以执行的时刻,所以就wait等待。
反之,如果 now >= msg.when ,说明现在的时刻已经到了可以发送消息的时刻,就可以把消息返回出去。
有没有想过这样一个问题:上面所说的next()函数由谁来调用的呢?
对,就是这个loop()函数,它不断的调用next()函数,通过调用next()函数去轮询我们的MessageQueue,让这个MessageQueue这样一个传送带滚动起来的。
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
}
如果这个消息队列为空的时候,会等待(MessageQueue.next())。
for (;;) {
.....
//native的方法,在没有消息的时候回阻塞管道读取端,只有nativePollOnce返回之后才能往下执行
//阻塞操作,等待nextPollTimeoutMillis时长
nativePollOnce(ptr, nextPollTimeoutMillis);
.....
}
所以它就一直会block在这里(Looper.loop())。
Message msg = queue.next(); // might block
被block之后就不会走return,不会走return,这个for循环(死循环)就一直在这里等待。
当Looper获取到了这个消息之后,就会调用dispatchMessage这个函数。
msg.target.dispatchMessage(msg);
而dispatchMessage又会调用Handler.dispatchMessage()来处理消息,这时候就完成了消息链条的整个流程。
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}