大部分同學在回答Handler的原理的時候都能回答出Handler將消息丟到MessageQueue中,然后Looper.loop死循環(huán)不斷從MessageQueue中拿消息去執(zhí)行亡资。
這塊我之前也有寫個文章介紹,如果忘了可以去看看。
但是如果再繼續(xù)追問Handler.postDelay又是怎么做到的就講不出來了扶欣。這里就給大家講一講辙售。
源碼解析
首先來看看handler里面是怎么處理postDelayed的:
public class Handler {
...
public final boolean postDelayed(Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
...
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
...
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
...
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
...
return enqueueMessage(queue, msg, uptimeMillis);
}
...
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
...
return queue.enqueueMessage(msg, uptimeMillis);
}
...
}
可以發(fā)現(xiàn)最后它也是把Runnable封裝成Message然后發(fā)給MessageQueue去處理的,所以我們繼續(xù)看看MessageQueue.enqueueMessage方法:
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 插入到隊列頭
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 按時間排序插入隊列
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; //如果不是插入隊列頭的話不需要喚醒線程,讓它繼續(xù)等到拿隊列頭的消息的時候再重新計算睡眠時間
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {
// 喚醒線程
nativeWake(mPtr);
}
}
return true;
}
這個方法的作用其實很簡單,按時間順序把Message插入MessageQueue,形成一個按時間排序的單鏈表,然后喚醒線程恕汇。
然后看看喚醒了什么線程?
我們都知道MessageQueue中的消息是由Looper.loop里面的一個死循環(huán)去讀取的槐脏。
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); // might block
...
}
...
}
這個這里還提示了MessageQueue.next方法也許會阻塞,所以我們看看next方法里面干了什么:
Message next() {
...
int nextPollTimeoutMillis = 0;
for (;;) {
...
//阻塞nextPollTimeoutMillis時間
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 跳過隊列前面的無用Message
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 {
//從隊列頭拿出Message
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
}
...
}
...
}
}
這里面有個native方法nativePollOnce,阻塞線程一段固定的時間,當然MessageQueue.enqueueMessage里面的nativeWake方法也能直接喚醒它此熬。
當有Message插入隊列頭的時候,就會喚醒線程鸡挠。然后MessageQueue.next方法就會拿出隊列頭的Message計算是否需要再等待一段時間去執(zhí)行廊勃。
舉個例子
代碼比較暈沒有關(guān)系,我們用一個簡單的例子把流程描述一下就好理解了懈贺。
首先假設隊列里面有兩個消息,分別在三秒坡垫、四秒之后執(zhí)行,也就是說MessageQueue.next的線程會睡眠三秒之后才去消息隊列拿隊列頭的消息:
此時,我們又post了一個一秒之后執(zhí)行的Message,于是它會被插入到隊列頭,然后MessageQueue.next的線程會被喚醒梭灿。但是拿到隊列頭的消息發(fā)現(xiàn)時間還沒有到,于是又會再睡眠一秒:
等了一秒之后MessageQueue.next的線程自己蘇醒拿出隊列頭的MessageC去分發(fā),然后繼續(xù)拿MessageA。但是發(fā)現(xiàn)時間又沒有到,于是又會再睡眠兩秒:
這個時候如果我們插入了一個立馬執(zhí)行的消息呢冰悠?它也是會插入到隊列頭,然后喚醒MessageQueue.next的線程,去隊列頭取消息執(zhí)行堡妒。執(zhí)行完之后又會拿MessageA。但是發(fā)現(xiàn)時間又沒有到,于是又會再睡眠兩秒溉卓。