面試復(fù)習(xí)篇---Handler原理分析

Handler發(fā)消息,最終調(diào)用enqueueMessage方法調(diào)用MessageQueued對(duì)象摸航,MessageQueue的enqueueMessage方法的是做對(duì)象鎖的同步操作匙监,next方法也是做了對(duì)象鎖的同步操作担锤,MessageQueue內(nèi)部有個(gè)mMessages(Message)對(duì)象不翩,這2個(gè)方法都是操作這個(gè)msg對(duì)象齿诞,所以線程是安全衡便,enqueueMessage是負(fù)責(zé)把消息存在message献起,next是負(fù)責(zé)把消息返回給Looper

enqueueMessage方法的部分代碼

      synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", 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;
            }

next方法的部分代碼

next方法:
        synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                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 (false) Log.v("MessageQueue", "Returning message: " + msg);
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

在Looper的loop方法通過for循環(huán)調(diào)用MessageQueue的next,獲取隊(duì)列的消息镣陕,最終會(huì)調(diào)用msg.target.dispatchMessage(msg)

    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 (;;) {
            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
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            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的dispatchMessage會(huì)把msg傳給handleCallback回調(diào)

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

看完源碼后谴餐,總結(jié)下
從handler中獲取一個(gè)消息對(duì)象,把數(shù)據(jù)封裝到消息對(duì)象中呆抑,通過handler的send…方法把消息push到MessageQueue隊(duì)列中岂嗓。
Looper對(duì)象會(huì)輪詢MessageQueue隊(duì)列,把消息對(duì)象取出鹊碍。
通過dispatchMessage分發(fā)給Handler厌殉,再回調(diào)用Handler實(shí)現(xiàn)的handleMessage方法處理消息

Handler的實(shí)現(xiàn)中適及以下對(duì)象:

1、Handler本身:負(fù)責(zé)消息的發(fā)送和處理
2侈咕、Message:消息對(duì)象
3公罕、MessageQueue:消息隊(duì)列(用于存放消息對(duì)象的數(shù)據(jù)結(jié)構(gòu))
4、Looper:消息隊(duì)列的處理者(用于輪詢消息隊(duì)列的消息對(duì)象耀销,取出后回調(diào)handler的dispatchMessage進(jìn)行消息的分發(fā)熏兄,dispatchMessage方法會(huì)回調(diào)handleMessage方法把消息傳入,由Handler的實(shí)現(xiàn)類來處理)

Message對(duì)象的內(nèi)部實(shí)現(xiàn)是鏈表树姨,最大長度是50摩桶,用于緩存消息對(duì)象,達(dá)到重復(fù)利用消息對(duì)象的目的帽揪,以減少消息對(duì)象的創(chuàng)建硝清,所以通常我們要使用obtainMessage方法來獲取消息對(duì)象

安全:

Handler的消息處理機(jī)制是線程安全的

關(guān)系:

創(chuàng)建Handler時(shí)會(huì)創(chuàng)建Looper,Looper對(duì)象的創(chuàng)建又創(chuàng)建了MessageQueue

Looper實(shí)現(xiàn)原理分析

內(nèi)部用ThreadLocal維持不同線程的Looper對(duì)象

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

ThreadLocal原理分析

ThreadLocal類用來提供線程內(nèi)部的局部變量转晰。這些變量在多線程環(huán)境下訪問(通過get或set方法訪問)時(shí)能保證各個(gè)線程里的變量相對(duì)獨(dú)立于其他線程內(nèi)的變量芦拿,ThreadLocal實(shí)例通常來說都是private static類型。
總結(jié):ThreadLocal不是為了解決多線程訪問共享變量查邢,而是為每個(gè)線程創(chuàng)建一個(gè)單獨(dú)的變量副本蔗崎,提供了保持對(duì)象的方法和避免參數(shù)傳遞的復(fù)雜性
ThreadLocal類中有一個(gè)靜態(tài)內(nèi)部類ThreadLocalMap(其類似于Map),用鍵值對(duì)的形式存儲(chǔ)每一個(gè)線程的變量副本扰藕,ThreadLocalMap中元素的key為當(dāng)前ThreadLocal對(duì)象缓苛,而value對(duì)應(yīng)線程的變量副本,每個(gè)線程可能存在多個(gè)ThreadLocal

引用鏈接

https://blog.csdn.net/blackzhangwei/article/details/51945516
https://blog.csdn.net/lhqj1992/article/details/52451136

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末邓深,一起剝皮案震驚了整個(gè)濱河市未桥,隨后出現(xiàn)的幾起案子笔刹,更是在濱河造成了極大的恐慌,老刑警劉巖冬耿,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舌菜,死亡現(xiàn)場離奇詭異,居然都是意外死亡亦镶,警方通過查閱死者的電腦和手機(jī)日月,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缤骨,“玉大人山孔,你說我怎么就攤上這事『杀铮” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵褐望,是天一觀的道長勒庄。 經(jīng)常有香客問我,道長瘫里,這世上最難降的妖魔是什么实蔽? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮谨读,結(jié)果婚禮上局装,老公的妹妹穿的比我還像新娘。我一直安慰自己劳殖,他們只是感情好铐尚,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著哆姻,像睡著了一般宣增。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上矛缨,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天爹脾,我揣著相機(jī)與錄音,去河邊找鬼箕昭。 笑死灵妨,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的落竹。 我是一名探鬼主播泌霍,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼述召!你這毒婦竟也來了烹吵?” 一聲冷哼從身側(cè)響起碉熄,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肋拔,沒想到半個(gè)月后锈津,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡凉蜂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年琼梆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窿吩。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡茎杂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出纫雁,到底是詐尸還是另有隱情煌往,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布轧邪,位于F島的核電站刽脖,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏忌愚。R本人自食惡果不足惜曲管,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望硕糊。 院中可真熱鬧院水,春花似錦、人聲如沸简十。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽螟蝙。三九已至橙喘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胶逢,已是汗流浹背厅瞎。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留初坠,地道東北人和簸。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像碟刺,于是被迫代替她去往敵國和親锁保。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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