Android正在使用Handler實現訊息分發機制(兩)

來源:互聯網
上載者:User

標籤:

在開始這篇文章之前,。首先,我們在總結前兩篇文章Handler, Looper和MessageQueue像一些關鍵點:

0)在建立線程Handler之前,你必須調用Looper.prepare(), 建立一個線程局部變數Looper,然後調用Looper.loop() 進入輪循。

1)當Handler建立之後,就能夠調用Handler的sendMessageAtTime方法發送訊息。而實際上是調用MessageQueue的enqueueMessage方法。將相應的訊息放入訊息佇列。

2)每個線程都僅僅有一個Looper,這個Looper負責對MessageQueue進行輪循,當擷取到Message。就調用handler.dispatchMessage進行分發。

從上面這三點,我們就能夠大概地看出Handler的使用流程了。

今天我們就先從訊息開始的地方講起。就是Handler的 enqueueMessage 方法了,代碼例如以下:

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

此方法主要做了兩件事:

1)將msg.target 設定成當前Handler對象

2)調用MessageQueue的enqueueMessage方法

所以。事實上就是在這裡。將Message對象放到訊息佇列中去的。

說到MessageQueue,我們首先要明確這個訊息佇列事實上是一個鏈表的結構,一個串一個的。

而其隊列的初始化並非在 java層做的。而是在JNI層利用C++實現的。

我們能夠看看其定義的幾個native方法,例如以下:

    private native static long nativeInit();    private native static void nativeDestroy(long ptr);    private native static void nativePollOnce(long ptr, int timeoutMillis);    private native static void nativeWake(long ptr);    private native static boolean nativeIsIdling(long ptr);

而其建構函式例如以下:

    MessageQueue(boolean quitAllowed) {        mQuitAllowed = quitAllowed;        mPtr = nativeInit();    }

在這裡,我們並不進入其在JNI層的代碼,水太深了。

我們還是從Java層來看吧。在MessageEnqueue的 enqueueMesage方法中。基本的代碼例如以下:

synchronized (this) {            ...            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;            }            ...        }


上面是入隊列的關鍵代碼,而其所做的操作無非就是依據 when 欄位。將訊息插入隊列中的合適位置。

既然訊息已經放到隊列中去了。那麼下一步就是在Looper的輪循操作中去擷取訊息,然後將訊息進行分發。我們能夠在Looper 的loop方法中看到其調用了MessageQueue的next方法。

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

那麼非常顯然。就是在MessageQueue的next方法中,擷取訊息了,讓我們也進去其方法看一下吧。

Message next() {        int pendingIdleHandlerCount = -1; // -1 only during first iteration        int nextPollTimeoutMillis = 0;        for (;;) {            if (nextPollTimeoutMillis != 0) {                Binder.flushPendingCommands();            }            // We can assume mPtr != 0 because the loop is obviously still running.            // The looper will not call this method after the loop quits.            nativePollOnce(mPtr, 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 (false) Log.v("MessageQueue", "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("MessageQueue", "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;        }    }


整個的代碼有點長,可是我們去掉一些對我們瞭解實現關係不大的代碼,就能夠看到主要有下面幾點:

1)是一個 for(;;)迴圈

2)僅僅有當擷取 message的時候或者mQuitting為true的時候才會跳出迴圈。

3)在擷取訊息的時候,會依據 Message.when 欄位來進行推斷

從以上幾點,我們就能夠大概瞭解為什麼說在 loop方法中,next方法有可能會堵塞。由於它就是一個無限的輪循操作呀。

好吧,到這裡之後,我們大概知道了下面兩件事情:

1)在Handler的sendMessageAtTime方法調用MessageQueue的 enqueueMessage方法,將訊息放入隊列。

2)在Looper的looop方法。調用MessageQueue的next方法。將訊息取出隊列。

接下來第三步,非常顯然,就是調用handler的dispatchMessage方法了,例如以下:

msg.target.dispatchMessage(msg);

在文章的一開始,我們就注意到了msg.target 正好就是 handler對象,於是邏輯又來到了Handler的dispatchMessage方法,例如以下:

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

從代碼的邏輯來看,我們須要瞭解一下幾個變數方法都是要什麼了:

1)msg.callback

2)  mCallback

3 ) handleMessage

首先, msg.callback是什嗎?

在Message類中。我們能夠看到

Runnable callback;

還有其建構函式:

    public static Message obtain(Handler h, Runnable callback) {        Message m = obtain();        m.target = h;        m.callback = callback;        return m;    }

事實上就是一個Runnable變數,能夠放到一個新的線程中去跑。能夠在擷取Message的時候自己定義設定。

所以在dispatchMessage中,首先就是推斷是否有對Message設定了Runnable的callback,假設有。就運行這個callback方法,例如以下:

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

那麼,第二個mCallback又是什麼呢,它事實上上是Handler內建的一個介面,例如以下:

    public interface Callback {        public boolean handleMessage(Message msg);    }

假設有我們的 Handler有實現這個介面,那麼當分發訊息的時候,此介面就會優先處理訊息。

而普通情況下,僅僅有我們想去繼承Handler類。實現自己定義的Handler的時候,我們才會去實現這個介面,而當此介面返回true的時候,Handler預設的handleMessage方法就不會再被調用了。反之,則依舊會調用。

最後,就是我們最普通的handleMessage方法了。也就是我們在實現一個最普通的handler的時候所實現的方法了。

相同。沒有範例怎麼能夠呢,請看代碼:

    class LooperThread extends Thread {        public Handler mHandler;        public void run() {            Looper.prepare();                mHandler = new Handler() {                                public void handleMessage(Message msg) {                    Log.v("Test", "Id of LooperThread : " + Thread.currentThread().getId());                    switch (msg.what) {                    case MSG_ID_1:                        Log.v("Test", "Toast called from Handler.sendMessage()");                        break;                    case MSG_ID_2:                        String str = (String) msg.obj;                        Log.v("Test", str);                        break;                    }                }            };            Looper.loop();        }    }        protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.v("Test", "Id of MainThread : " + Thread.currentThread().getId());                LooperThread looperThread = new LooperThread();        looperThread.start();                       while(looperThread.mHandler == null){                                }                Message message = Message.obtain(looperThread.mHandler, new Runnable() {                        @Override            public void run() {                Log.v("Test", "Message.callack()");            }        });        message.what = MSG_ID_1;        message.sendToTarget();                looperThread.mHandler.post(new Runnable() {                        @Override            public void run() {                Log.v("Test", "Handler.callack()");            }        });    }

在這裡。我們利用Message.obtain(Handler, Runnable)  方法和Handler.post方法來構造和發送訊息,得到的結果例如以下:

10-28 11:27:49.328: V/Test(22009): Id of MainThread : 110-28 11:27:49.328: V/Test(22009): Message.callack()10-28 11:27:49.328: V/Test(22009): Handler.callack()

好了,這篇文章就到此結束,相信大家對整個Message的流轉過程,應該有它的一個清醒的認識。


著作權聲明:本文部落格原創文章,部落格,未經同意,不得轉載。

Android正在使用Handler實現訊息分發機制(兩)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.