Handler運行機制

Handler運行機制是Android消息處理機制的上層接口. 依靠Looper, MessageQueue, Message支撐/協做.html

在主線程中不能放置耗時任務, 不然會引發ANR. 因此通常耗時任務會放在子線程當中. 因爲Android開發規範, 在子線程中不能進行UI操做. 不可避免地涉及進行線程之間通訊問題. 因此有人說, Handler運行機制就是用來處理UI線程與子線程之間的通訊的. 這僅僅是Handler運行機制的一個應用場景. 好比還能夠實現子線程向子線程發送消息, 能夠參考下這篇文章.java

四個重要角色

Message

Message是消息的載體, 比較重要的兩個字段是objwhat字段, obj就是須要傳遞消息的內容, what標誌消息的類型, 方便在接收的時候處理.bash

MessageQueue

存儲Meesage的單鏈表, 向外提供讀取next方法, 與enqueueMessage入隊操做.less

Looper

維護MessageQueue, 開始工做時, 不斷從MessageQueue中取出Message分發給他們的target(其實就是他們對應的handler). 一個線程中只能有一個Looper對象.async

Handler

消息的發送者與消息的處理者ide

一個經典的例子

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();
    }
}
複製代碼

這裏經過繼承Thread,建立了一個LooperThread的線程類, run方法中先調用了Looper.prepare(), 而後建立了一個Handler對象, 最後調用了Looper.loop()方法.oop

接下來, 咱們經過源碼分析看看究竟發生了什麼.源碼分析

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方法能夠重載, 先會判斷sThreadLocal.get()是否爲空, 爲空的話, 先new Looper(quitAllowed)建立了一個Looper對象, 而後把這個Looper對象保存在了sThreadLocal中. 能夠想象當咱們再次在當前線程調用Looper.prepare方法時, 這時的sThreadLocal.get()就不爲空了, 會向咱們拋出一個Only one Looper may be created per thread異常. 由此能夠保證咱們每一個線程最多擁有一個Looper對象.post

剛纔構造Looper對象的過程當中, 究竟又作了什麼呢? 咱們看看Looper的構造方法ui

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

能夠看到內容只有兩行. 分別爲Looper的兩個成員變量賦值, 建立了一個MessageQueue, Looper綁定了當前線程.

總結下就是: Looper.prepare方法在當前線程中建立了一個Looper對象, Looper對象的建立又致使了MessageQueue對象建立. 而且綁定當前線程. (Looper和MessageQueue在一個線程中最多有一個)

建立Handler對象

Handler對象的建立咱們直接來看Handler的構造方法.

/** * 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的構造方法有好幾個, 其實作的工做, 不外乎爲他的各個成員變量賦值mLooper,mQueue,mCallback,mAsynchronous. 分析最複雜的Handler(Callback callback, boolean async)方法. Looper.myLooper()方發得到了一個Looper對象, 這個Looper對象是哪裏來的呢?

/** * 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(), 原來就是從咱們在Looper.prepare()中存起來的Looper, 若是爲空, 說明咱們的prepare方法根本沒有執行. 拋出Can't create handler inside thread that has not called Looper.prepare()異常. 接下來Handler的構造方法還作了一件事, 把Looper中維護的MessageQueue取出來賦值給了mQueue字段.

總結下: 獲取當前線程的Looper對象取出來, 並把他和他維護的MessageQueue賦值給了Handler的成員變量.

這裏有個問題: void handleMessage(Message msg)又是怎樣被調用的呢?別急, 讓咱們看看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的構造方法同樣, 先得到當前線程得Looper對象. 若是爲空, 那必定又是沒有prepare. 接下來能夠看到for (;;) {}這樣一個結構, 一個死循環, 不斷獲取nextMessage, 直到Message爲空.

這裏有個問題: 一直在說從隊列中不斷取Message, Message是多久放入隊列的?

Message的入隊時經過Handler對象的sendxxx類與postxxx類方法實現的.

  • sendxxx類
//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);
    }
    //最後都調用到這裏
    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);
    }
複製代碼

能夠看到最後都執行了boolean sendMessageAtTime(Message msg, long uptimeMillis)方法. 能夠看到這裏最終調用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);
        }
複製代碼

這就時咱們想要的入隊操做. 值得留意的是這裏的msg.target = this, 入隊的Message標記了發送他的Handler.

  • postxxx方法 postxxx方法都用兩個相同的特徵: 都傳入Runnable對象, 都最終調用了一個sendxxx方法. 因此說postxxx方法最終仍是會調用enqueueMessage(queue, msg, uptimeMillis)讓消息入隊. 好像有一點不對? 明明傳入的一個Runnable對象, 可是入隊的時候, 存入其中卻變成了Message?咱們來看看其中一個postxxx方法.
public final boolean postDelayed(Runnable r, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }
複製代碼

能夠看到這裏多調用了一個getPostMessage(r)方法. 這個方法就是將咱們的Runnable對象封裝爲Message對象的關鍵.

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

能夠看到這裏得到一個Message後, 將Message的callback字段設置爲了Ruannable對象. 這下就豁然開朗了.

接下來接着看在MessageQueue中擁有Handler發送來的消息後, 會如何進行操做. 在死循環中. msg.target.dispatchMessage(msg) 讓msg的target(也就是發送他的Handler)去分發事件.

/** * 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就是再這裏最後調用的. 除此以外, 消息的分發還有兩條路徑msg.callbackmCallback.handleMessage(msg). msg.callback還記得嗎?就是postxxx類消息發送的Runnable對象. mCallback.handleMessage(msg)中的mCallback則是在Handler重載的構造方法的參數. 這裏一旦設置了回調,而且其handlerMessage返回值爲true, 就能夠實現對Hadnler的handlerMessage的攔截.

ps: 有什麼疏漏或者錯誤的地方還請各位指出.

參考:

相關文章
相關標籤/搜索