Handler流程梳理

理一下Handler消息分發(fā)流程缨叫,一定離不開這幾個(gè)類:
Handler:用于消息發(fā)送和接收
Message:消息
MessageQueue:消息隊(duì)列算柳,用于存儲消息
Looper:循環(huán)取出從MessageQueue中的Message

分三點(diǎn)說起擂红,Handler的創(chuàng)建土砂、消息的發(fā)送乏悄、消息的接收

一龙亲、Handler的創(chuàng)建

首先從new Handler開始看看Handler初始化的時(shí)候都做了什么:

    //Handler源碼
    public Handler(Callback callback, boolean async) {
        ...

        mLooper = Looper.myLooper();
        ...
        mQueue = mLooper.mQueue;
        mCallback = callback;
        ...
    }

內(nèi)容很簡單陕凹,mLooper、mQueue鳄炉、mCallback的賦值杜耙,mCallback沒什么說的,我們初始化的時(shí)候傳遞過來的:

    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            //消息處理
            return false;
        }
    });

那這里的mLooper是怎么來的拂盯?而且mQueue的賦值也是取的mLooper.mQueue佑女,跟進(jìn)去這個(gè)Looper.myLooper()方法:

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

關(guān)于ThreadLocal在Handler常見疑問的最后寫過了,這里需要疑問的是sThreadLocal什么時(shí)候set的。主線程的Looper是在ActivityThread中創(chuàng)建的团驱,ActivityThread是App一啟動就會調(diào)用的摸吠,去從ActivityThread的main方法開始跟起:

    public static void main(String[] args) {
        ...

        Looper.prepareMainLooper();

        ...
        Looper.loop();

        ...
    }

先跟這個(gè)prepareMainLooper()方法:

    public static void prepareMainLooper() {
        prepare(false);
       ...
    }
    ...
    private static void prepare(boolean quitAllowed) {
        ...
        sThreadLocal.set(new Looper(quitAllowed));
    }

到這里就已經(jīng)找到了主線程中Looper的創(chuàng)建,是在APP一啟動就創(chuàng)建了嚎花。然后再看看Looper.loop();做了什么:

    public static void loop() {
        final Looper me = myLooper();
        ...
        final MessageQueue queue = me.mQueue;

        ...

        for (;;) {
            Message msg = queue.next(); // might block
            ...
            msg.target.dispatchMessage(msg);
            ...
            msg.recycleUnchecked();
        }
    }

這里的for是一個(gè)無限循環(huán)寸痢,不斷的從MessageQueue中取出消息并且分發(fā),這段代碼先留著贩幻,一會到消息發(fā)送的時(shí)候再具體看轿腺,先看前兩句两嘴,獲取Looper并且獲取Looper中的MessageQueue丛楚,myLooper()剛才看過了,還是調(diào)用sThreadLocal.get();獲取的Looper對象憔辫,那MessageQueue是什么時(shí)候創(chuàng)建趣些,這里的me是個(gè)Looper的實(shí)例,那mQueue肯定是Looper的一個(gè)全局屬性贰您,mQueue是在Looper的構(gòu)造函數(shù)中賦值的:

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

這個(gè)牽扯出個(gè)跟主流程無關(guān)的問題坏平,構(gòu)造函數(shù)會發(fā)現(xiàn)是私有的,為什么這樣處理锦亦?那是怎么提供給外界的舶替?
上面其實(shí)已經(jīng)貼過了:

    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        ...
        sThreadLocal.set(new Looper(quitAllowed));
    }

就是這個(gè)prepare()方法,還是個(gè)靜態(tài)的杠园,在ActivityThread的main函數(shù)中調(diào)用了顾瞪,這個(gè)結(jié)合Handler常見疑問中的ThreadLocal介紹,就很容易理解了抛蚁,只要是同一個(gè)線程中陈醒,調(diào)用sThreadLocal.get();獲取的就是同一個(gè)對象。這樣保證主線程中的Looper只有一個(gè)瞧甩,MessageQueue作為Looper的屬性肯定也只有一個(gè)钉跷。
總結(jié)一下new Handler其實(shí)很簡單:

  • 得到當(dāng)前線程中的Looper對象(本篇以主線程為例)
  • 得到Looper中的MessageQueue
  • 接收調(diào)用者傳遞過來的Callback

到這里,必須理清楚的有:

  • 主線程的Looper在App啟動的時(shí)候已經(jīng)創(chuàng)建好了肚逸,而MessageQueue作為Looper的全局屬性也已經(jīng)在構(gòu)造函數(shù)中創(chuàng)建了爷辙,并且Looper已經(jīng)開始無限循環(huán)從MessageQueue中取消息,雖然現(xiàn)在可能沒有消息
  • Looper是通過ThreadLocal.get()獲取的朦促,所以線程唯一膝晾,而MessageQueue作為Looper的全局屬性也是線程唯一
  • Handler創(chuàng)建之后已經(jīng)持有了當(dāng)前線程(本篇以主線程Handler為例)唯一的Looper、MessageQueue和調(diào)用者傳遞過來的Callback

二思灰、消息發(fā)送

Handler創(chuàng)建好之后玷犹,從消息的發(fā)送開始分析源碼,使用代碼:

        new Thread(new Runnable() {
            @Override
            public void run() {
                handler.sendMessage(handler.obtainMessage());
            }
        }).start();

直接點(diǎn)進(jìn)去sendMessage()

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        ...
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        ...
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        ...
        return queue.enqueueMessage(msg, uptimeMillis);
    }

這都是Handler里的方法,一路跟下來首先在sendMessageAtTime()方法中獲取了MessageQueue為mQueue歹颓,這里先確定一下坯屿,雖然是在子線程調(diào)用的sendMessage()方法,但是這個(gè)handler還是主線程中的handler巍扛,這個(gè)一定要清楚领跛,所以根據(jù)Handler創(chuàng)建過程的分析可以肯定這里的mQueue是主線程中的MessageQueue,然后在enqueueMessage ()方法中撤奸,調(diào)用了queue.enqueueMessage()把這個(gè)消息壓入了主線程的MessageQueue中吠昭,跟這個(gè)方法之前還有個(gè)注意點(diǎn)就是這個(gè)msg.target = this,在壓入消息之前胧瓜,把handler本身設(shè)置給了msg矢棚,也就是msg也持有了hander,這個(gè)target只是取的一個(gè)名字府喳,他的類型就是Handler:

public final class Message implements Parcelable {
    ...
    Handler target;
    ...
}

繼續(xù)到MessageQueue中跟上面的壓入方法queue.enqueueMessage()

    boolean enqueueMessage(Message msg, long when) {
        ...
        ...
        Message p = mMessages;
        if (p == null || when == 0 || when < p.when) {
            ...
            mMessages = msg;
            ...
        } 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.
            ...
        }
        ...
    }

這里的if...else...可以簡單理解為如果當(dāng)前隊(duì)列里沒有消息并且要發(fā)送的這個(gè)消息沒有延遲蒲肋,就直接賦值給mMessages,否則就加入到消息隊(duì)列中钝满。
我們跟流程這里不需要看那么復(fù)雜兜粘,就直接走if中的流程,也就是說我們要發(fā)送的這個(gè)消息已經(jīng)賦值給了MessageQueue的全局屬性mMessages弯蚜。
到這里消息發(fā)送流程完事孔轴,再總結(jié)一下上面的流程:

  1. 通過主線程的Handler發(fā)送一條message
  2. handler的sendMessage()方法最終是調(diào)用了queue.enqueueMessage(message, ...),并且這個(gè)message的target指向handler碎捺,這里的queue就是主線程的MessageQueue
  3. MessageQueue的enqueueMessage(message, ...)方法接收到這個(gè)message并且復(fù)制給了自己的全局屬性mMessages

三路鹰、消息接收

    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            //消息處理
            return false;
        }
    });

消息接收就是要看handler的handleMessage(Message msg)上面時(shí)候被調(diào)用,也就是
handler什么時(shí)候收到回調(diào)牵寺。
在第一步Handler的創(chuàng)建的時(shí)候悍引,Loop里有一個(gè)無限循環(huán),就是從MessageQueue中取消息的帽氓,在第二步消息發(fā)送的時(shí)候已經(jīng)把Message放入了MessageQueue中(本篇流程設(shè)定場景是已經(jīng)把要發(fā)送的Message設(shè)置給了MessageQueue中的mMessages)趣斤。所以現(xiàn)在就從Looper.loop這個(gè)無限循環(huán)開始看起,還是Handler創(chuàng)建時(shí)候跟到的這段代碼:

    public static void loop() {
        final Looper me = myLooper();
        ...
        final MessageQueue queue = me.mQueue;

        ...

        for (;;) {
            Message msg = queue.next(); // might block
            ...
            msg.target.dispatchMessage(msg);
            ...
            msg.recycleUnchecked();
        }
    }

這里通過queue.next()獲取到Message對象黎休,并切通過msg.target.dispatchMessage(msg)發(fā)送消息浓领。先看queue.next()代碼:

    Message next() {
        ...       
        for (;;) {
            ...
            synchronized (this) {
                ...
                Message msg = mMessages;
                ...
                if (msg != null) {
                    ...
                    return msg;
                    ...
                } else {
                   ...
                }
               ...
        }
    }

這個(gè)next()返回一個(gè)Message對象,而這個(gè)對象正是剛才發(fā)送消息時(shí)候賦值的mMessages势腮,這樣就是會說已經(jīng)拿到了剛才要發(fā)送的對象联贩。

這個(gè)函數(shù)里...的部分是非本篇邏輯的代碼,就是上面我們發(fā)送消息的時(shí)候沒考慮多條待處理消息和延遲消息捎拯,所以這里也不考慮MessageQueue消息堆積的邏輯泪幌,再解釋一下就是消息沒堆積,MessageQueue中只有一條,發(fā)送的時(shí)候賦值給mMessages祸泪,取的時(shí)候從mMessages里取吗浩。其實(shí)這里如果發(fā)送消息的時(shí)候發(fā)現(xiàn)mMessages有值會有另外的邏輯去存消息,取的時(shí)候也就需要相應(yīng)的邏輯去取没隘,本篇主要梳理Handler工作方式懂扼,不展開消息隊(duì)列的具體工作方式。

拿到消息之后是用過msg.target.dispatchMessage(msg);發(fā)送的右蒲,而這個(gè)target上面分析過其實(shí)就是handler阀湿,直接去看Handler的dispatchMessage(msg)的方法:

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

這里一看就熟悉了handleMessage(msg)被調(diào)用了,也就是handler拿到了回調(diào)瑰妄,收到消息陷嘴,流程走完。
這個(gè)dispatchMessage()方法還有要說的翰撑,這里發(fā)現(xiàn)有兩重if...else判斷罩旋,首先我們的message沒有設(shè)置callback,我們設(shè)置的是handler的callback眶诈,所以直接進(jìn)來第一次判斷的else里,這個(gè)問題在Handler常見疑問的Handler兩種創(chuàng)建方式里已經(jīng)寫的很明白瓜饥。那這個(gè)第一層if里的邏輯msg.callback流程是怎么樣的逝撬?這就牽扯出了下一個(gè)問題

四、Handler的post()方法

        new Thread(new Runnable() {
            @Override
            public void run() {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this, "我是被post出來的吐司", Toast.LENGTH_LONG).show();
                    }
                });
            }
        }).start();

這種使用也不陌生乓土,跟這個(gè)post()方法看看:

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

看看這個(gè)getPostMessage(r)對Runnable做了什么操作:

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

他把Runnable封裝成了一個(gè)Message宪潮,并且返回,也就是說post (Runnable r)方法把傳遞過來的Runnable封裝成了一個(gè)Message趣苏,然后調(diào)用了sendMessageDelayed(message, ...)方法狡相,而這個(gè)Message的callback屬性指向了這個(gè)Runnable,關(guān)于sendMessageDelayed(message, ...)方法不用繼續(xù)跟了食磕,上面消息發(fā)送的時(shí)候已經(jīng)分析的透透的了尽棕。到這里就可以繼續(xù)消息接收中的那個(gè)問題:

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

這里的if條件就是這樣進(jìn)來的,然后繼續(xù)下去這個(gè)handleCallback(msg);彬伦,跟之前再說一下滔悉,這個(gè)msg的callback屬性是指向子線程中傳遞過來的一個(gè)Runnable,明確之后繼續(xù)跟handleCallback(msg)

    private static void handleCallback(Message message) {
        message.callback.run();
    }

非常簡單单绑,就一句回官,調(diào)用了message中runnable的run()方法。結(jié)合上面的消息發(fā)送和接收搂橙,總結(jié)一下:

  1. 在子線程中通過handler.post(runnable)發(fā)送了一個(gè)Runnable對象
  2. 把這個(gè)Runnable對象封裝成Message對象放入到MessageQueue中
  3. 主線程的Looper對象在無限從MessageQueue中取消息歉提,會取出這個(gè)帶有Runnable的Message
  4. 取出后執(zhí)行Message對象中Runnable的run()方法

再仔細(xì)想一下這個(gè)post()方法,這就是Handler實(shí)現(xiàn)線程切換的流程,本例中Handler苔巨、Loop弯屈、MessageQueue都是主線程中的,子線程里把一個(gè)Runnable對象封裝成Message對象放入到主線程Loop對象的MessageQueue恋拷,直到這個(gè)Message被取出资厉,調(diào)用了message.runnable.run()

完事蔬顾。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宴偿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子诀豁,更是在濱河造成了極大的恐慌窄刘,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舷胜,死亡現(xiàn)場離奇詭異娩践,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)烹骨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門翻伺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人沮焕,你說我怎么就攤上這事吨岭。” “怎么了峦树?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵辣辫,是天一觀的道長。 經(jīng)常有香客問我魁巩,道長急灭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任谷遂,我火速辦了婚禮葬馋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘埋凯。我一直安慰自己点楼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布白对。 她就那樣靜靜地躺著掠廓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪甩恼。 梳的紋絲不亂的頭發(fā)上蟀瞧,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天沉颂,我揣著相機(jī)與錄音,去河邊找鬼悦污。 笑死铸屉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的切端。 我是一名探鬼主播彻坛,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼踏枣!你這毒婦竟也來了昌屉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤茵瀑,失蹤者是張志新(化名)和其女友劉穎间驮,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體马昨,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡竞帽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鸿捧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屹篓。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖笛谦,靈堂內(nèi)的尸體忽然破棺而出抱虐,到底是詐尸還是另有隱情,我是刑警寧澤饥脑,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站懦冰,受9級特大地震影響灶轰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜刷钢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一笋颤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧内地,春花似錦伴澄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至荆针,卻和暖如春敞嗡,著一層夾襖步出監(jiān)牢的瞬間颁糟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工喉悴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留棱貌,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓箕肃,卻偏偏與公主長得像婚脱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子勺像,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

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