從源碼角度看handler 消息機(jī)制

[toc]
涉及的類(lèi)主要有這四個(gè)
Message
MessageQueue
Looper
Handler

Message 消息對(duì)象

1、 主要屬性 what obj target 缓艳,主要講講target
target 就是一個(gè)發(fā)送和處理消息是綁定的一個(gè)handler對(duì)象邑蒋,它是在sendMessage時(shí)指定成需要發(fā)送消息的那個(gè)handler, 在Looper.loop()中被調(diào)用去處理消息

MessageQueue 消息隊(duì)列

1、 主要有兩個(gè)方法 enqueueMessage() 入隊(duì) 和 next() 出隊(duì)兩個(gè)方法蜡塌。
enqueueMessage() : 在handler.sendMessage 最終其實(shí)就是調(diào)用的就是MessageQueue.enqueueMessage()古今,你可以理解這個(gè)方法是一個(gè)消息鏈表添加一個(gè)message對(duì)象.那它是什么時(shí)候回創(chuàng)建的呢蠕搜?

//handler.sendMessage 最終其實(shí)就是調(diào)用的就是MessageQueue.enqueueMessage()
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this; //上面說(shuō)的指定了target
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    //queue 是什么時(shí)候創(chuàng)建的轮蜕?看Handler構(gòu)造方法
    return queue.enqueueMessage(msg, uptimeMillis);
}

//Handler 構(gòu)造方法
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue; // 在創(chuàng)建handler的時(shí)候就指定了MseeageQueue對(duì)象的實(shí)例昨悼,從這里也可以知道MessageQueue實(shí)例是在Looper對(duì)象里面。
    mCallback = callback;
    mAsynchronous = async;
}

截止目前跃洛,還是沒(méi)有回答上一個(gè)問(wèn)題率触,什么時(shí)候創(chuàng)建的queue。我們繼續(xù)看下一個(gè)對(duì)象Looper

Looper 消息泵

這個(gè)對(duì)象其實(shí)就是一個(gè)消息泵汇竭,不停的在處理消息葱蝗。我們需要關(guān)心的兩個(gè)方法 Looper.prepare() 和 Looper.loop()
1、Looper.prepare()

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));//sThreadLocal 內(nèi)部使用了一個(gè)類(lèi)似于map來(lái)維護(hù)
}

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

可以看到這個(gè)Only one Looper may be created per thread 细燎,一個(gè)線(xiàn)程只能創(chuàng)建一個(gè)looper 两曼,然后調(diào)用了sThreadLocal.set(new Looper(quitAllowed)) 在Looper對(duì)象的構(gòu)造方法中,就會(huì)創(chuàng)建一個(gè)Messagequeue對(duì)象

2玻驻、Looper.loop()
這個(gè)是loop()方法的全部?jī)?nèi)容

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;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) { // 無(wú)限for循環(huán)悼凑,不停的獲取消息,調(diào)用target對(duì)象身上的dispatchMessage
        //出隊(duì)獲取到消息對(duì)象
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        final long end;
        try {
            // 獲取到綁定在Message消息身上的Handler對(duì)象璧瞬,調(diào)用它的dispatchMessage方法户辫,處理消息
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        if (slowDispatchThresholdMs > 0) {
            final long time = end - start;
            if (time > slowDispatchThresholdMs) {
                Slog.w(TAG, "Dispatch took " + time + "ms on "
                        + Thread.currentThread().getName() + ", h=" +
                        msg.target + " cb=" + msg.callback + " msg=" + msg.what);
            }
        }

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}

可能大家還是很疑惑,我們使用handler時(shí)彪蓬,從來(lái)沒(méi)有創(chuàng)建過(guò)Looper.prepare方法寸莫,那么looper對(duì)象實(shí)例是什么時(shí)候被創(chuàng)建的呢捺萌?
其實(shí)在創(chuàng)建UI線(xiàn)程(ActivityThread )時(shí)档冬,就已經(jīng)創(chuàng)建了Looper對(duì)象

public static void main(String[] args) {
    /...
    Process.setArgV0("<pre-initialized>");

    Looper.prepareMainLooper(); //內(nèi)部調(diào)用了Looper.prepare

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();

}

4、Handler 消息處理者
截止這里就可以看到Handler的工作機(jī)制了桃纯,sendMessage發(fā)送消息酷誓,其實(shí)就是一個(gè)入隊(duì)操作,而dispatchMessage就是處理消息态坦,我們通常的handleMessage就是在這里被調(diào)用的盐数。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

截止目前我們可以大概理解Handler消息機(jī)制大概是這樣的:
在UI線(xiàn)程被創(chuàng)建時(shí),ActivityThread 的main方法就會(huì)創(chuàng)建一個(gè)Looper對(duì)象伞梯,并啟動(dòng)消息泵Looper.loop()玫氢,并創(chuàng)建一個(gè)MessageQueue對(duì)象,在我們創(chuàng)建Handler的時(shí)候谜诫,獲取到Looper對(duì)象和它身上的queue對(duì)象漾峡,并在Handler.sendMessage()方法調(diào)用mQueue對(duì)象的入隊(duì)方法。Looper.loop()內(nèi)部維護(hù)這一個(gè)無(wú)限for循環(huán)喻旷,不斷的從queue.next() 方法生逸,獲取到Messge對(duì)象并調(diào)用msg.target.dispatchMessage(msg)處理消息。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市槽袄,隨后出現(xiàn)的幾起案子烙无,更是在濱河造成了極大的恐慌,老刑警劉巖遍尺,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件截酷,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡乾戏,警方通過(guò)查閱死者的電腦和手機(jī)合搅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)歧蕉,“玉大人灾部,你說(shuō)我怎么就攤上這事」咄耍” “怎么了赌髓?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)催跪。 經(jīng)常有香客問(wèn)我锁蠕,道長(zhǎng),這世上最難降的妖魔是什么懊蒸? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任荣倾,我火速辦了婚禮,結(jié)果婚禮上骑丸,老公的妹妹穿的比我還像新娘舌仍。我一直安慰自己,他們只是感情好通危,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布铸豁。 她就那樣靜靜地躺著,像睡著了一般菊碟。 火紅的嫁衣襯著肌膚如雪节芥。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,610評(píng)論 1 305
  • 那天逆害,我揣著相機(jī)與錄音头镊,去河邊找鬼。 笑死魄幕,一個(gè)胖子當(dāng)著我的面吹牛相艇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播梅垄,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼厂捞,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼输玷!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起靡馁,我...
    開(kāi)封第一講書(shū)人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤欲鹏,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后臭墨,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體赔嚎,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年胧弛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了尤误。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡结缚,死狀恐怖损晤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情红竭,我是刑警寧澤尤勋,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站茵宪,受9級(jí)特大地震影響最冰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜稀火,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一暖哨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧凰狞,春花似錦篇裁、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至斩熊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伐庭,已是汗流浹背粉渠。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留圾另,地道東北人霸株。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像集乔,于是被迫代替她去往敵國(guó)和親去件。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355

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