Handler進(jìn)擊之探索sendMessage的奇妙

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)題关带。

  1. sendMessageDelayed是如何實(shí)現(xiàn)延時(shí)發(fā)送消息的侥涵?
  2. sendMessageDelayed是通過(guò)阻塞來(lái)達(dá)到了延時(shí)發(fā)送消息的結(jié)果,那么會(huì)不會(huì)阻塞新添加的Message宋雏?

總結(jié):

  1. Handler在發(fā)送消息的時(shí)候芜飘,MessageQueue里的消息是按照發(fā)送時(shí)間點(diǎn)從小到大排列的,
    如果最近的Message未到達(dá)發(fā)送的時(shí)間則阻塞磨总。
  2. 新加入的數(shù)據(jù)會(huì)根據(jù)時(shí)間點(diǎn)的大小判斷需要插入的位置嗦明,同時(shí)還需要判斷是否需要喚醒線程去發(fā)送當(dāng)前的隊(duì)首的消息。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蚪燕,一起剝皮案震驚了整個(gè)濱河市娶牌,隨后出現(xiàn)的幾起案子奔浅,更是在濱河造成了極大的恐慌,老刑警劉巖诗良,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件汹桦,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡鉴裹,警方通過(guò)查閱死者的電腦和手機(jī)舞骆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)径荔,“玉大人督禽,你說(shuō)我怎么就攤上這事〔荩” “怎么了赂蠢?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)辨泳。 經(jīng)常有香客問(wèn)我虱岂,道長(zhǎng),這世上最難降的妖魔是什么菠红? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任第岖,我火速辦了婚禮,結(jié)果婚禮上试溯,老公的妹妹穿的比我還像新娘蔑滓。我一直安慰自己,他們只是感情好遇绞,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布键袱。 她就那樣靜靜地躺著,像睡著了一般摹闽。 火紅的嫁衣襯著肌膚如雪蹄咖。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,337評(píng)論 1 310
  • 那天付鹿,我揣著相機(jī)與錄音澜汤,去河邊找鬼。 笑死舵匾,一個(gè)胖子當(dāng)著我的面吹牛俊抵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播坐梯,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼徽诲,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起谎替,我...
    開(kāi)封第一講書(shū)人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤轩拨,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后院喜,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡晕翠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年喷舀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片淋肾。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡硫麻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出樊卓,到底是詐尸還是另有隱情拿愧,我是刑警寧澤,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布碌尔,位于F島的核電站浇辜,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏唾戚。R本人自食惡果不足惜柳洋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望叹坦。 院中可真熱鬧熊镣,春花似錦、人聲如沸募书。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)莹捡。三九已至鬼吵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間道盏,已是汗流浹背而柑。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留荷逞,地道東北人媒咳。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像种远,于是被迫代替她去往敵國(guó)和親涩澡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容