前言
我們經(jīng)常用Handler中的postDelayed方法進行延遲操作溢吻,像這樣
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//操作內(nèi)容
}
},100);
我們都知道Handler的機制是將消息通過sendMessage()放入到MessageQueen中 然后looper輪訓(xùn)器通過輪訓(xùn)取出MessageQueen中的消息回調(diào)目標(biāo)Handler中的handlerMessage()。
可是postDelayed(),這個方法他的底層是怎么做的呢停撞?是延遲把消息放入到MessageQueen的嗎,點進去看看,發(fā)現(xiàn)不是
一.源碼分析
1.點進去看postDelayed()中的方法。里面調(diào)用sendMessageDelayed方法备蚓,和post() 里面調(diào)用的方法一樣。
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
2.我們再點進去看下sendMessageDelayed()方法,
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
里面調(diào)用了sendMessageAtTime()囱稽,這里的SystemClock.uptimeMillis()是獲取系統(tǒng)從開機啟動到現(xiàn)在的時間,期間不包括休眠的時間,這里獲得到的時間是一個相對的時間,而不是通過獲取當(dāng)前的時間(絕對時間)郊尝。
而之所以使用這種方式來計算時間,而不是獲得當(dāng)前currenttime來計算粗悯,在于handler會受到阻塞虚循,掛起狀態(tài),睡眠等样傍,這些時候是不應(yīng)該執(zhí)行的横缔;如果使用絕對時間的話,就會搶占資源來執(zhí)行當(dāng)前handler的內(nèi)容衫哥,顯然這是不應(yīng)該出現(xiàn)的情況茎刚,所以要避免。
3.點進sendMessageAtTime()方法看看
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);
}
追到這里依然沒有看到撤逢,他在存放的時候有什么不同膛锭,但是顯然證實了消息不是延遲放進MessageQueen的,那是腫么處理的蚊荣,是在輪訓(xùn)的時候處理的嗎初狰?,
4.我們點進Looper中看一下互例,主要代碼奢入,Looper中的looper調(diào)用了MessageQueen中的next方法,難道是在next()方法中處理的媳叨?
public static void loop() {
···
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
···
5.我們點進MessageQueen中的next()方法
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
···
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);
} else {
// Got a message.
mBlocked = false;
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;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
···
}
}
很貼心的給出了注釋解釋“ Next message is not ready. Set a timeout to wake up when it is ready.”腥光,翻譯“下一條消息尚未準(zhǔn)備好关顷。設(shè)置一個超時,以便在準(zhǔn)備就緒時喚醒武福∫樗”
when就是uptimeMillis, for (;;) 相當(dāng)于while(true)捉片,如果頭部的這個Message是有延遲而且延遲時間沒到的(now < msg.when)平痰,不返回message 而且會計算一下時間(保存為變量nextPollTimeoutMillis),然后再循環(huán)的時候判斷如果這個Message有延遲伍纫,就調(diào)用nativePollOnce(ptr, nextPollTimeoutMillis)進行阻塞觉增。nativePollOnce()的作用類似與object.wait()。得出結(jié)論是通過阻塞實現(xiàn)的翻斟。
6.但是如果在阻塞這段時間里有無延遲message又加入MessageQueen中又是怎么實現(xiàn)立即處理這個message的呢?说铃,我們看MessageQueen中放入消息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;
}
在這里p 是現(xiàn)在消息隊列中的頭部消息访惜,我們看到| when < p.when 的時候它交換了放入message與原來消息隊列頭部P的位置,并且 needWake = mBlocked; (在next()中當(dāng)消息為延遲消息的時候mBlocked=true)腻扇,繼續(xù)向下看 當(dāng)needWake =true的時候nativeWake(mPtr)(喚起線程)
一切都解釋的通了债热,如果當(dāng)前插入的消息不是延遲message,或比當(dāng)前的延遲短幼苛,這個消息就會插入頭部并且喚起線程來
二.整理
我們把我們跟蹤的所有信息整理下
1.消息是通過MessageQueen中的enqueueMessage()方法加入消息隊列中的窒篱,并且它在放入中就進行好排序,鏈表頭的延遲時間小舶沿,尾部延遲時間最大
2.Looper.loop()通過MessageQueue中的next()去取消息
3.next()中如果當(dāng)前鏈表頭部消息是延遲消息墙杯,則根據(jù)延遲時間進行消息隊列會阻塞,不返回給Looper message括荡,知道時間到了高镐,返回給message
4.如果在阻塞中有新的消息插入到鏈表頭部則喚醒線程
5.Looper將新消息交給回調(diào)給handler中的handleMessage后,繼續(xù)調(diào)用MessageQueen的next()方法畸冲,如果剛剛的延遲消息還是時間未到嫉髓,則計算時間繼續(xù)阻塞
三總結(jié)
handler.postDelay() 的實現(xiàn) 是通過MessageQueue中執(zhí)行時間順序排列,消息隊列阻塞邑闲,和喚醒的方式結(jié)合實現(xiàn)的算行。
如果真的是通過延遲將消息放入到MessageQueen中,那放入多個延遲消息就要維護多個定時器苫耸,