Handler是如何实现延迟消息的,这是个老生常谈的问题了。
这里我就带大家从源码的角度看看,并把handler各方面实现查漏补缺一下。
handler核心的发送消息的方法是sendMessage,有的朋友会说那post呢?
post的话其实算是一个handler的语法糖,传入runnable后帮助我们构建一个message。
/**
* Causes the Runnable r to be added to the message queue.
* The runnable will be run on the thread to which this handler is
* attached.
*
* @param r The Runnable that will be executed.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
可以看到getPostMessage里帮我们构建出一个message然后再调用sendMessageDelayed。
接下来看sendMessage,类似于startActivity最终都会走到startActivityForResult一样,handler所有发送消息的方法最终都会走到sendMessageDelayed,只是delayMillis不同而已,这个delayMillis就是延时的时间。
/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
然后这里会将DelayMillis加上当前开机的时间(这里可以理解就是这个time就是,现在的时间+需要延迟的时间=实际执行的时间),接下来进到sendMessageAtTime方法里面
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
其实到这里handler的任务就完成了,把message发送到messageQueue里面,每个消息都会带有一个uptimeMillis参数,这就是延时的时间。
接下来我们看messageQueue里面queue.enqueueMessage这个方法。
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) {
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) {
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;
}
首先是进行msg的一些属性判断,handler发出的target值必须不为空,是为了通过target值来判断是哪个handler发过来的消息的。
顺便说一说 并不是所有的msg,target值都必须不为空
(handler的同步屏障就是一个target为空的msg,用来优先执行异步方法的)
同步屏障有一个很重要的使用场所就是接受垂直同步Vsync信号,用来刷新页面view的。因为为了保证view的流畅度,所以每次刷新信号到来的时候,要把其他的任务先放一放,优先刷新页面。
接下来主要就是将这个msg根据实际执行时间进行排序插入到queue里面(看里面的for循环)。
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
好了,现在queue也构建完成了,假设我现在第一条消息就是要延迟10秒,怎么办呢。实际走一边咯。
假设我现在是looper,我要遍历这个messageQueue,那肯定要调用next方法。
next()方法比较长,我只贴关于延时消息的核心部分。
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);
}
………………以下省略
可以看到这里也是一个for循环遍历队列,核心变量就是nextPollTimeoutMillis。可以看到,计算出nextPollTimeoutMillis后就调用nativiePollOnce这个native方法。这里的话大概可以猜到他的运行机制,因为他是根据执行时间进行排序的,那传入的这个nextPollTimeoutMillis应该就是休眠时间,类似于java的sleep(time)。休眠到下一次message的时候就执行。那如果我在这段时间又插入了一个新的message怎么办,所以handler每次插入message都会唤醒线程,重新计算插入后,再走一次这个休眠流程。
nativiePollOnce这个native方法可以通过名字知道,他用的是linux中的epoll机制,具体是调用了epoll_wait这个方法。
int epoll_wait(int epfd, struct epoll_event * events, intmaxevents, int timeout);
这个epoll和select一样都是linux的一个I/O多路复用机制,主要原理就不深入了,这里大概了解一下I/O多路复用机制和它与Select的区别就行。
Linux里的I/O多路复用机制:举个例子就是我们钓鱼的时候,为了保证可以最短的时间钓到最多的鱼,我们同一时间摆放多个鱼竿,同时钓鱼。然后哪个鱼竿有鱼儿咬钩了,我们就把哪个鱼竿上面的鱼钓起来。这里就是把这些全部message放到这个机制里面,那个time到了,就执行那个message。
epoll与select的区别:epoll获取事件的时候采用空间换时间的方式,类似与事件驱动,有哪个事件要执行,就通知epoll,所以获取的时间复杂度是O(1),select的话则是只知道有事件发生了,要通过O(n)的事件去轮询找到这个事件。
仅供参考,欢迎指正