Android_基礎(chǔ)_進階_Handler 消息機制

Android 的消息機制主要由 Handler,Looper,MessageQueue,Message 等組成,而 Handler 的運行主要依賴后三者;

源碼分析

Handler 的消息處理主要有五個部分, Message, Handler, MessageQueue, Looper, Threadlocal.

方法 作用
Messager Message 是線程之間傳遞的消息,他可以在內(nèi)部攜帶少量的數(shù)據(jù),用于線程之間交換數(shù)據(jù), Message 有四個常用字段,what,arg1,arg2,obj.其中what,arg1,arg2可以攜帶整型數(shù)據(jù),而 obj 可以攜帶 object 對象.
Handler 它主要用于發(fā)送和處理消息的發(fā)送消息一般使用的是sendMessage() 方法,還有其他一系列的sendXXX方法,但是最終都是調(diào)用了 sendMessageAtTime() 方法, 除了 sendMessageAtTime() 這個方法而發(fā)出的消息經(jīng)過一系列的輾轉(zhuǎn)處理后,最終會傳遞到 Handler的handleMessage() 方法中。
MessageQueue MessageQqueue 是消息隊列的意思,它主要用來存放所有通過 Handler 發(fā)送的消息,這部分的消息會一直存在消息隊列中,按進入隊列的時間順序依次等待被處理. 每一個線程中都會有一個 MessageQueue 對象.
Looper 每個線程通過 Handler 發(fā)送的消息都保存在 MessageQueue 中,而 Looper 通過調(diào)用 loop() 方法,就會進入一個無線循環(huán)中,然后發(fā)現(xiàn)一個 MessageQueue 中存在一條消息就將它取出來,并傳遞給 Handler.handlermessage() 方法中.每一個線程只會存在一個 Looper 對象
ThreadLocal MessageQueue 對象,和 Looper 對象在每個線程中都會有一個對象,那么我們怎么保證他只有一個對象呢?單利?靜態(tài)?其實使用通過 ThreadLocal 來保存.ThreadLocal 是一個線程內(nèi)部的數(shù)據(jù)類,他可以在指定線程中存儲數(shù)據(jù),數(shù)據(jù)存儲后,只能由指定的線程獲取,而其他的線程則不可獲取到數(shù)據(jù).

在了解到這些基本的概念以后我們就來深入的看一看 Handler 的工作機制.

MessageQueue 的工作原理

MessageQueue 消息隊列是通過一個單鏈表的數(shù)據(jù)結(jié)構(gòu)來維護消息鏈表.下面最主要看 enqueueMessage 方法和 next() 方法.

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

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

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

可以看出來,這個方法主要是根據(jù)時間順序向表單中插入一條消息.
那么 next() 又是用來干什么呢?
我們繼續(xù)看下去:

Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            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 (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

在 next 中有一個關(guān)鍵方法 for (;;) 對就是這個死循環(huán),慢慢梳理代碼我們不難看出如果有消息返回就從鏈表中移除.沒有消息的時候就會一直柱塞在這里

Looper

在一個 Android 啟動應(yīng)用的時候,會創(chuàng)建一個主線程, 也就是UI線程.而這個主線程 ActivityThread 中的一個靜態(tài)的 main 方法. 這個 main 方法也就是我們應(yīng)用程序的入口點. 我們來簡單的看一下這個 main 方法.

public static void main(String[] args) {

    ......

    Looper.prepareMainLooper();

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

    ......

    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

從上面的方法中我們可以看出來,使用了 Looper.prepareMainLooper() 方法為主線程創(chuàng)建了 Looper 以及 MessageQueue, 并通過Looper.loop() 來開心主線程的消息循環(huán).

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

這個方法中調(diào)用了 prepare(false);方法和 myLooper(); 方法

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 對象保存了一個 Looper 對象,以防止被調(diào)用兩次.sThreadLocal 對象是 ThreadLocal 類型,因此保證了每個線程中只有一個 Looper 對象。
那么 Looper 到底干了什么?

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

果不其然,在 Looper 的構(gòu)造函數(shù)中創(chuàng)建一個 MessageQueue 對象和保存了當前的線程.從之前的代碼中可以看出一個線程只能有一個 Looper 對象,而 MessageQueue 又是在 Looper 構(gòu)造函數(shù)中創(chuàng)建出來的,因此每一個線程也只有一個 MessageQueue 對象;
還有一個 prepare 的重載方法;

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

prepare() 僅僅是對 prepare(boolean quitAllowed) 的封裝,這里就是解釋了主線程不需要調(diào)用 Looper.prepare() 方法了.因為主線程在啟動的時候已經(jīng)很貼心的幫我們調(diào)用了啊!

然后在說說我們在 Looper.perpareMainLooper() 方法中的 myLooper()

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

這串英文是什么意思呢? 讓我們請出我們可愛谷歌娘:返回與當前線程關(guān)聯(lián)的Looper對象。 如果調(diào)用線程未與Looper關(guān)聯(lián),則返回null。
有沒有很清楚一下就明白了.就是取出我們當前線程中的 Looper 對象,保存在 sMainLooper 瞧瞧這命名方式多簡單易懂;

我是不是忘記了什么?
哦!對了還有一個方法: Looper.loop() main 還調(diào)用了這個方法是用來干什么的呢?

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.recycle();
        }
}

快看是 next() 方法,是不是好像在剛剛的 MessageQueue 方法中看到了?
對就是這樣,從這個方法里進入一個無限循環(huán),不斷的從 MessageQueue 的 next 方法獲取消息,而next方法是一個阻塞操作,當沒有消息的時候就一直在阻塞,當有消息通過 msg.target.dispatchMessage(msg); 這里的msg.target其實就是發(fā)送給這條消息的Handler對象。那么我們就去看看 Handle 又干了什么;

Handler

構(gòu)造方法

public Handler(Callback callback) {
    this(callback, false);
}

public Handler(Looper looper) {
    this(looper, null, false);
}

public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
}

怎么都要傳遞 Looper 我們沒有怎么辦,那就看看不需要傳遞 Looper 的構(gòu)造函數(shù)

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

什么? 當 Looper 為 null 竟然拋出了 Can't create handler inside thread that has not called Looper.prepare() 異常,這樣我們知道為什么在子線程中使用 Handler 時要手動調(diào)用 Looper.prepare() 方法了,原來是用來創(chuàng)建一個 Looper 對象.那么主線程為什么不用呢?因為服務(wù)周到的主線程已經(jīng)在創(chuàng)建的時候就自己調(diào)用了 Looper.prepare() 方法了.

Handler 的工作主要事實包含發(fā)送和接收過程.其中 post 和 send 的一系列方法主要是用倆發(fā)送消息.但是 post 其實最終也會通過 ssend 的一系列方法來實現(xiàn)的. 而 send 的一系列方法最終會通過 sendMessageAtTime 方法來實現(xiàn).來我們看看 send 的一系列方法:

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }  

    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }

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

    public final boolean sendMessageAtFrontOfQueue(Message msg) {
        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, 0);
    }

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

這里我們可以看出,handler 發(fā)送的消息其實就是將消息插入消息隊列,在 Looper 的 loop 方法中從 MessageQueue 中取出并調(diào)用 msg.target.dispatchMessage(msg) ;而這個其實就是就是在調(diào)用 Handler 的 dispatchMessage(msg) :

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

先最一個非空判斷,非空時調(diào)用了 handleCallback(msg); 來處理消息,那么 msg.callback 是什么東西啊? 其實這里的 msg.callback 就是一個 Runnable對象, 也就是 Handler 發(fā)送過來的 post 對象.先看看 post 的對象的方法;

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

 public final boolean postAtTime(Runnable r, long uptimeMillis)
{
    return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}

public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
{
    return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}


public final boolean postDelayed(Runnable r, long delayMillis)
{
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}


public final boolean postAtFrontOfQueue(Runnable r)
{
    return sendMessageAtFrontOfQueue(getPostMessage(r));
}

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

好了我們在看看 msg.callback 非空時 handleCallback(msg) 是做了什么:

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

emmmmm... 果然就是很簡單的回調(diào)了 Runnable 對象的 run 方法. 其實吧我們?nèi)タ纯?Activity 中的 runOnUiThread 和 View 中的 postDelayed 方法也是使用了同樣的原理,我們先看看 runOnUiThread 方法:

public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

View 的 postDelayed 方法:

public boolean postDelayed(Runnable action, long delayMillis) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        return attachInfo.mHandler.postDelayed(action, delayMillis);
    }
    // Assume that post will succeed later
    ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
    return true;
}

實質(zhì)上都是在 UI 線程中執(zhí)行 Runnable 中的 run 方法.

在回來看看 msg.callback 為空的時候會對 mCallback 進行非空判斷,而 mCallback 又使用一個接口的引用:

/**
 * Callback interface you can use when instantiating a Handler to avoid
 * having to implement your own subclass of Handler.
 *
 * @param msg A {@link android.os.Message Message} object
 * @return True if no further handling is desired
 */
public interface Callback {
    public boolean handleMessage(Message msg);
}

原來 CallBack 其實就是另一種使用 Handler 的方式啊, 看來既可以派生子類重寫 handleMessage() 的方法也可以通過設(shè)置 CallBack 來實現(xiàn).

總結(jié)

首先在主線程創(chuàng)建一個 Handler 對象 ,并重寫 handleMessage()方法。然后當在子線程中需要進行更新 UI 的操作,我們就創(chuàng)建一個 Message 對象,并通過 handler 發(fā)送這條消息出去。之后這條消息被加入到 MessageQueue 隊列中等待被處理,通過 Looper 對象會一直嘗試從 MessageQueue 中取出待處理的消息,最后分發(fā)會 Handler 的 handlerMessage() 方法中。

最后來一張我話的流程圖:

Handler流程.png

有沒有很美觀.
END..

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容