Handler進(jìn)階之sendMessage
?本文主要進(jìn)一步的探索Handler,主要介紹下Handler是如何發(fā)送消息的?
?用過(guò)Handler的想必對(duì)一下幾個(gè)方法都不會(huì)陌生:
sendMessage(Message msg);//立刻發(fā)送消息
sendMessageAtTime(Message msg, long atTime);//在某個(gè)時(shí)間點(diǎn)發(fā)送消息
sendMessageDelayed(Message msg, long delayedTime);//在當(dāng)前時(shí)間點(diǎn)延遲一段時(shí)間發(fā)送消息
?以上是三個(gè)Handler發(fā)送消息的方法,區(qū)別在于發(fā)送的時(shí)間點(diǎn)不一致,但其實(shí)三個(gè)方法在最終都是執(zhí)行Handler內(nèi)的同一個(gè)方法苛让,只是在參數(shù)上稍有區(qū)別:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
sendMessage(Message msg);
//對(duì)應(yīng)
sendMessageDelayed(msg, 0);
sendMessageAtTime(Message msg, long atTime);
//對(duì)應(yīng)
sendMessageDelayed(msg, atTime)
sendMessageDelayed(Message msg, long delayedTime);
//對(duì)應(yīng)
sendMessageDelayed(msg, SystemClock.uptimeMillis() + delayedTime)
?從上面的代碼能夠看出湿诊,最終的消息發(fā)送都是傳入了要發(fā)送Message對(duì)象和對(duì)應(yīng)需要發(fā)送Message的時(shí)間點(diǎn)狱杰。
我們猜想,Handler發(fā)送消息難道是根據(jù)Message需要發(fā)送的時(shí)間點(diǎn)設(shè)置一個(gè)定時(shí)器讓Message在某個(gè)時(shí)間點(diǎn)被發(fā)送出去厅须?
如果僅此而已的話仿畸,你也太小看google的大牛們了。是不是已經(jīng)有點(diǎn)迫不及待想直到大牛們都是怎么來(lái)做的了朗和?
?這里將中間調(diào)用的幾個(gè)簡(jiǎn)單的過(guò)程跳過(guò)错沽,熟悉Java的一眼就能看懂,直接進(jìn)入發(fā)送消息時(shí)最核心的部分眶拉,
我們都知道Handler發(fā)送消息其實(shí)是將Message先放入到了MessageQueue千埃,能看到該文章的我默認(rèn)大家都已經(jīng)很熟悉
Handler的基本原理了,如果又不熟悉的忆植,可以參考
Handler消息機(jī)制原理全方文解讀
?此處直接看Message放入MessageQueue的過(guò)程:
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) {
// 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;
}
?重點(diǎn)就在if-else這里,將它們一個(gè)一個(gè)拆開(kāi)來(lái)看:
if (p == null || when == 0 || when < p.when) {//p是當(dāng)前MessageQueue隊(duì)首Message
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
}
?如果當(dāng)前隊(duì)列沒(méi)有其他需要發(fā)送的Message朝刊;或者當(dāng)前新添加進(jìn)來(lái)的的Message的時(shí)間點(diǎn)為0(即需要立即發(fā)送的消息)吴侦;
或者當(dāng)前新添加進(jìn)來(lái)的的Message需要發(fā)送的時(shí)間點(diǎn)小與當(dāng)前MessageQueue隊(duì)列頭部Message的時(shí)間點(diǎn)(即當(dāng)前添加進(jìn)來(lái)的Message需要在當(dāng)前MessageQueue隊(duì)列頭部Message之前被發(fā)送)時(shí),就會(huì)進(jìn)入到if代碼塊里面坞古。此時(shí)做的事情是將當(dāng)前新添加的Message插入到了MessageQueue的隊(duì)首(看不懂是如何插入的可以參考Handler消息機(jī)制原理全方文解讀,
Message 的存儲(chǔ)是鏈表的形式劫樟,next相當(dāng)于鏈表的尾指針)痪枫。
?這里還有一個(gè)賦值操作(needWake = mBlocked)织堂,這里解釋下,可以看到代碼里也有注釋奶陈,新的首部易阳,然后如果阻塞了,需要喚醒線程吃粒。為什么會(huì)有線程的阻塞呢潦俺?其實(shí)MessageQueue內(nèi)部的消息是按需要發(fā)送的時(shí)間點(diǎn)從小到大排列的,后面會(huì)分析到徐勃,從當(dāng)前if里的when判斷也能看出一二事示,當(dāng)隊(duì)首的Message未到達(dá)發(fā)送的時(shí)間點(diǎn)時(shí),說(shuō)明其當(dāng)前所有的消息都未到達(dá)發(fā)送的時(shí)間僻肖,上面說(shuō)過(guò)肖爵,Handler發(fā)送消息并不是通過(guò)定時(shí)器發(fā)送的,所以臀脏,當(dāng)隊(duì)首Message(最近需要發(fā)送的Message)未到達(dá)發(fā)送時(shí)間點(diǎn)時(shí)劝堪,線程被阻塞,所以這里需要根據(jù)線程是否阻塞看是否需要喚醒線程揉稚,這樣才能使新加入的Message能及時(shí)發(fā)送出去秒啦,不會(huì)被阻塞。線程的喚醒是通過(guò)native的方法來(lái)實(shí)現(xiàn)的搀玖。
?接著來(lái)看下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;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
?執(zhí)行到了else語(yǔ)句里面余境,說(shuō)明了當(dāng)前添加進(jìn)來(lái)的Message是在當(dāng)前MessqgeQueue隊(duì)首的Message之后才會(huì)被發(fā)送的,上邊分析if部分代碼的時(shí)候說(shuō)過(guò)了Message是按需要發(fā)送的時(shí)間先后排列在MessageQueue中的巷怜,這里的for循環(huán)實(shí)際操作就是找到MessageQueue中比當(dāng)前添加進(jìn)來(lái)的Message需要發(fā)送的時(shí)間點(diǎn)大的位置葛超,將Message插入到其前邊(實(shí)際就是一個(gè)鏈表的插入操作)。
?本章分析到這里就先告一段落了延塑,之后會(huì)再詳細(xì)的分析Message從MessageQueue的取出過(guò)程绣张,可以先參考Handler消息機(jī)制原理全方文解讀。
之所以寫(xiě)這篇文章主要是為了解答兩個(gè)問(wèn)題关带。
- sendMessageDelayed是如何實(shí)現(xiàn)延時(shí)發(fā)送消息的侥涵?
- sendMessageDelayed是通過(guò)阻塞來(lái)達(dá)到了延時(shí)發(fā)送消息的結(jié)果,那么會(huì)不會(huì)阻塞新添加的Message宋雏?
總結(jié):
- Handler在發(fā)送消息的時(shí)候芜飘,MessageQueue里的消息是按照發(fā)送時(shí)間點(diǎn)從小到大排列的,
如果最近的Message未到達(dá)發(fā)送的時(shí)間則阻塞磨总。 - 新加入的數(shù)據(jù)會(huì)根據(jù)時(shí)間點(diǎn)的大小判斷需要插入的位置嗦明,同時(shí)還需要判斷是否需要喚醒線程去發(fā)送當(dāng)前的隊(duì)首的消息。