Handler運(yùn)行機(jī)制

Handler運(yùn)行機(jī)制是Android消息處理機(jī)制的上層接口. 依靠Looper, MessageQueue, Message支撐/協(xié)作.

在主線(xiàn)程中不能放置耗時(shí)任務(wù), 否則會(huì)引起ANR. 所以一般耗時(shí)任務(wù)會(huì)放在子線(xiàn)程當(dāng)中. 由于Android開(kāi)發(fā)規(guī)范, 在子線(xiàn)程中不能進(jìn)行UI操作. 不可避免地涉及進(jìn)行線(xiàn)程之間通信問(wèn)題. 所以有人說(shuō), Handler運(yùn)行機(jī)制就是用來(lái)處理UI線(xiàn)程與子線(xiàn)程之間的通信的. 這僅僅是Handler運(yùn)行機(jī)制的一個(gè)應(yīng)用場(chǎng)景. 比如還可以實(shí)現(xiàn)子線(xiàn)程向子線(xiàn)程發(fā)送消息, 可以參考下這篇文章.

四個(gè)重要角色

Message

Message是消息的載體, 比較重要的兩個(gè)字段是objwhat字段, obj就是需要傳遞消息的內(nèi)容, what標(biāo)志消息的類(lèi)型, 方便在接收的時(shí)候處理.

MessageQueue

存儲(chǔ)Meesage的單鏈表, 向外提供讀取next方法, 與enqueueMessage入隊(duì)操作.

Looper

維護(hù)MessageQueue, 開(kāi)始工作時(shí), 不斷從MessageQueue中取出Message分發(fā)給他們的target(其實(shí)就是他們對(duì)應(yīng)的handler). 一個(gè)線(xiàn)程中只能有一個(gè)Looper對(duì)象.

Handler

消息的發(fā)送者與消息的處理者

一個(gè)經(jīng)典的例子

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
         Looper.prepare();
         mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }
}

這里通過(guò)繼承Thread,創(chuàng)建了一個(gè)LooperThread的線(xiàn)程類(lèi), run方法中先調(diào)用了Looper.prepare(), 然后創(chuàng)建了一個(gè)Handler對(duì)象, 最后調(diào)用了Looper.loop()方法.

接下來(lái), 我們通過(guò)源碼分析看看究竟發(fā)生了什么.

Looper.Prepare

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

    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));
    }

可以看到prepare方法可以重載, 先會(huì)判斷sThreadLocal.get()是否為空, 為空的話(huà), 先new Looper(quitAllowed)創(chuàng)建了一個(gè)Looper對(duì)象, 然后把這個(gè)Looper對(duì)象保存在了sThreadLocal中. 可以想象當(dāng)我們?cè)俅卧诋?dāng)前線(xiàn)程調(diào)用Looper.prepare方法時(shí), 這時(shí)的sThreadLocal.get()就不為空了, 會(huì)向我們拋出一個(gè)Only one Looper may be created per thread異常. 由此可以保證我們每個(gè)線(xiàn)程最多擁有一個(gè)Looper對(duì)象.

剛才構(gòu)造Looper對(duì)象的過(guò)程中, 究竟又做了什么呢?
我們看看Looper的構(gòu)造方法

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

可以看到內(nèi)容只有兩行. 分別為L(zhǎng)ooper的兩個(gè)成員變量賦值, 創(chuàng)建了一個(gè)MessageQueue, Looper綁定了當(dāng)前線(xiàn)程.

總結(jié)下就是: Looper.prepare方法在當(dāng)前線(xiàn)程中創(chuàng)建了一個(gè)Looper對(duì)象, Looper對(duì)象的創(chuàng)建又導(dǎo)致了MessageQueue對(duì)象創(chuàng)建. 并且綁定當(dāng)前線(xiàn)程. (Looper和MessageQueue在一個(gè)線(xiàn)程中最多有一個(gè))

創(chuàng)建Handler對(duì)象

Handler對(duì)象的創(chuàng)建我們直接來(lái)看Handler的構(gòu)造方法.

       /**
         * Default constructor associates this handler with the {@link Looper} for the
         * current thread.
         *
         * If this thread does not have a looper, this handler won't be able to receive messages
         * so an exception is thrown.
         */
        public Handler() {
            this(null, false);
        }
    
        /**
         * Constructor associates this handler with the {@link Looper} for the
         * current thread and takes a callback interface in which you can handle
         * messages.
         *
         * If this thread does not have a looper, this handler won't be able to receive messages
         * so an exception is thrown.
         *
         * @param callback The callback interface in which to handle messages, or null.
         */
        public Handler(Callback callback) {
            this(callback, false);
        }
    
        /**
         * Use the provided {@link Looper} instead of the default one.
         *
         * @param looper The looper, must not be null.
         */
        public Handler(Looper looper) {
            this(looper, null, false);
        }
    
        /**
         * Use the provided {@link Looper} instead of the default one and take a callback
         * interface in which to handle messages.
         *
         * @param looper The looper, must not be null.
         * @param callback The callback interface in which to handle messages, or null.
         */
        public Handler(Looper looper, Callback callback) {
            this(looper, callback, false);
        }
    
        /**
         * Use the {@link Looper} for the current thread
         * and set whether the handler should be asynchronous.
         *
         * Handlers are synchronous by default unless this constructor is used to make
         * one that is strictly asynchronous.
         *
         * Asynchronous messages represent interrupts or events that do not require global ordering
         * with respect to synchronous messages.  Asynchronous messages are not subject to
         * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
         *
         * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
         * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
         *
         * @hide
         */
        public Handler(boolean async) {
            this(null, async);
        }
    
        /**
         * Use the {@link Looper} for the current thread with the specified callback interface
         * and set whether the handler should be asynchronous.
         *
         * Handlers are synchronous by default unless this constructor is used to make
         * one that is strictly asynchronous.
         *
         * Asynchronous messages represent interrupts or events that do not require global ordering
         * with respect to synchronous messages.  Asynchronous messages are not subject to
         * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
         *
         * @param callback The callback interface in which to handle messages, or null.
         * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
         * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
         *
         * @hide
         */
        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;
            mCallback = callback;
            mAsynchronous = async;
        }
    
        /**
         * Use the provided {@link Looper} instead of the default one and take a callback
         * interface in which to handle messages.  Also set whether the handler
         * should be asynchronous.
         *
         * Handlers are synchronous by default unless this constructor is used to make
         * one that is strictly asynchronous.
         *
         * Asynchronous messages represent interrupts or events that do not require global ordering
         * with respect to synchronous messages.  Asynchronous messages are not subject to
         * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
         *
         * @param looper The looper, must not be null.
         * @param callback The callback interface in which to handle messages, or null.
         * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
         * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
         *
         * @hide
         */
        public Handler(Looper looper, Callback callback, boolean async) {
            mLooper = looper;
            mQueue = looper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }

可以看到Handler的構(gòu)造方法有好幾個(gè), 其實(shí)做的工作, 不外乎為他的各個(gè)成員變量賦值mLooper,mQueue,mCallback,mAsynchronous. 分析最復(fù)雜的Handler(Callback callback, boolean async)方法. Looper.myLooper()方發(fā)獲得了一個(gè)Looper對(duì)象, 這個(gè)Looper對(duì)象是哪里來(lái)的呢?

       /**
         * Return the Looper object associated with the current thread.  Returns
         * null if the calling thread is not associated with a Looper.
         */
        public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }

查看源碼看到sThreadLocal.get(), 原來(lái)就是從我們?cè)?code>Looper.prepare()中存起來(lái)的Looper, 如果為空, 說(shuō)明我們的prepare方法根本沒(méi)有執(zhí)行. 拋出Can't create handler inside thread that has not called Looper.prepare()異常. 接下來(lái)Handler的構(gòu)造方法還做了一件事, 把Looper中維護(hù)的MessageQueue取出來(lái)賦值給了mQueue字段.

總結(jié)下: 獲取當(dāng)前線(xiàn)程的Looper對(duì)象取出來(lái), 并把他和他維護(hù)的MessageQueue賦值給了Handler的成員變量.

這里有個(gè)問(wèn)題: void handleMessage(Message msg)又是怎樣被調(diào)用的呢?別急, 讓我們看看Looper.loop()方法.

Looper.loop()

       /**
         * Run the message queue in this thread. Be sure to call
         * {@link #quit()} to end the loop.
         */
        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
                final Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                }
    
                final long traceTag = me.mTraceTag;
                if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                    Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
                }
                try {
                    msg.target.dispatchMessage(msg);
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
    
                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的構(gòu)造方法一樣, 先獲得當(dāng)前線(xiàn)程得Looper對(duì)象. 如果為空, 那一定又是沒(méi)有prepare. 接下來(lái)可以看到for (;;) {}這樣一個(gè)結(jié)構(gòu), 一個(gè)死循環(huán), 不斷獲取nextMessage, 直到Message為空.

這里有個(gè)問(wèn)題: 一直在說(shuō)從隊(duì)列中不斷取Message, Message是多久放入隊(duì)列的?

Message的入隊(duì)時(shí)通過(guò)Handler對(duì)象的sendxxx類(lèi)與postxxx類(lèi)方法實(shí)現(xiàn)的.

  • sendxxx類(lèi)
 //sendEmptyMessageDelayed ==>  sendMessageDelayed
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
    //sendMessageDelayed==>  sendMessageAtTime
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    //最后都調(diào)用到這里
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

可以看到最后都執(zhí)行了boolean sendMessageAtTime(Message msg, long uptimeMillis)方法. 可以看到這里最終調(diào)用enqueueMessage(queue, msg, uptimeMillis).

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

這就時(shí)我們想要的入隊(duì)操作. 值得留意的是這里的msg.target = this, 入隊(duì)的Message標(biāo)記了發(fā)送他的Handler.

  • postxxx方法
    postxxx方法都用兩個(gè)相同的特征: 都傳入Runnable對(duì)象, 都最終調(diào)用了一個(gè)sendxxx方法. 所以說(shuō)postxxx方法最終還是會(huì)調(diào)用enqueueMessage(queue, msg, uptimeMillis)讓消息入隊(duì). 好像有一點(diǎn)不對(duì)? 明明傳入的一個(gè)Runnable對(duì)象, 但是入隊(duì)的時(shí)候, 存入其中卻變成了Message?我們來(lái)看看其中一個(gè)postxxx方法.
    public final boolean postDelayed(Runnable r, long delayMillis)
    {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

可以看到這里多調(diào)用了一個(gè)getPostMessage(r)方法. 這個(gè)方法就是將我們的Runnable對(duì)象封裝為Message對(duì)象的關(guān)鍵.

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

可以看到這里獲得一個(gè)Message后, 將Message的callback字段設(shè)置為了Ruannable對(duì)象. 這下就豁然開(kāi)朗了.

接下來(lái)接著看在MessageQueue中擁有Handler發(fā)送來(lái)的消息后, 會(huì)如何進(jìn)行操作. 在死循環(huán)中. msg.target.dispatchMessage(msg) 讓msg的target(也就是發(fā)送他的Handler)去分發(fā)事件.

       /**
         * Handle system messages here.
         */
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }

這里的邏輯就非常清晰了, 剛才想弄清楚的Handler的handleMessage就是再這里最后調(diào)用的. 除此之外, 消息的分發(fā)還有兩條路徑msg.callbackmCallback.handleMessage(msg). msg.callback還記得嗎?就是postxxx類(lèi)消息發(fā)送的Runnable對(duì)象. mCallback.handleMessage(msg)中的mCallback則是在Handler重載的構(gòu)造方法的參數(shù). 這里一旦設(shè)置了回調(diào),并且其handlerMessage返回值為true, 就可以實(shí)現(xiàn)對(duì)Hadnler的handlerMessage的攔截.

ps: 有什么疏漏或者錯(cuò)誤的地方還請(qǐng)各位指出.

參考:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末诉字,一起剝皮案震驚了整個(gè)濱河市胰伍,隨后出現(xiàn)的幾起案子鲸拥,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件让虐,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)璧帝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)葵诈,“玉大人裸弦,你說(shuō)我怎么就攤上這事∽鞔” “怎么了理疙?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)泞坦。 經(jīng)常有香客問(wèn)我窖贤,道長(zhǎng),這世上最難降的妖魔是什么贰锁? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任赃梧,我火速辦了婚禮,結(jié)果婚禮上豌熄,老公的妹妹穿的比我還像新娘授嘀。我一直安慰自己,他們只是感情好锣险,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布蹄皱。 她就那樣靜靜地躺著,像睡著了一般芯肤。 火紅的嫁衣襯著肌膚如雪巷折。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,730評(píng)論 1 289
  • 那天崖咨,我揣著相機(jī)與錄音锻拘,去河邊找鬼。 笑死击蹲,一個(gè)胖子當(dāng)著我的面吹牛署拟,可吹牛的內(nèi)容都是我干的婉宰。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼推穷,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼芍阎!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起缨恒,我...
    開(kāi)封第一講書(shū)人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤谴咸,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后骗露,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體岭佳,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年萧锉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了珊随。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡柿隙,死狀恐怖叶洞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情禀崖,我是刑警寧澤衩辟,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站波附,受9級(jí)特大地震影響艺晴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜掸屡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一封寞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧仅财,春花似錦狈究、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至风喇,卻和暖如春宁改,著一層夾襖步出監(jiān)牢的瞬間缕探,已是汗流浹背魂莫。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留爹耗,地道東北人耙考。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓谜喊,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親倦始。 傳聞我的和親對(duì)象是個(gè)殘疾皇子斗遏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

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