1. 程式人生 > >有關Handler機制原理的總結

有關Handler機制原理的總結

Handler是執行緒與執行緒間進行通訊的一套機制。

       Handler是常被開發者拿來更新UI的一種訊息處理機制,它的執行機制需要底層的Looper和MessageQueue的支撐。

       一個Android應用程式被建立時就會建立一個程序,該程序用應用的包名作為程序名。該程序會啟動主執行緒ActivityThread,也叫做UI主執行緒,但有時需要做些耗時操作,為了不能夠去阻塞UI主執行緒的正常執行,我們將它放在子執行緒中進行操作,操作完成後需要繪製UI,但Android子執行緒不能直接操作UI執行緒的,所以通過Handler來進行通訊。

      為什麼Android子執行緒不能直接操作主執行緒?Android UI執行緒不是執行緒安全的,如果多執行緒併發的話就會造成介面混亂,不可控的狀態。那為什麼不能讓主程式加上鎖機制,這樣就能夠執行緒安全了?可上鎖就會有造成訪問的邏輯變得很麻煩、很複雜,並且會阻塞其他執行緒的執行。綜上問題,Android採用單執行緒模型來處理UI操作。

Handler機制原理

       Handler機制是由Looper和MessageQueue來構建訊息機制的。

       MessageQueue:訊息佇列。雖然名為佇列,但事實上它的內部儲存結構並不是真正的佇列,而是採用單鏈表的資料結構來儲存訊息列表的,其中主要有插入enqueue()和從中拿走並刪除next()兩個方法。

       Looper:訊息迴圈。MessageQueue來儲存訊息,Looper則是以無限迴圈的方式去查詢是否有新訊息,如有就去處理,若沒有就standby(等待)。一個執行緒建立Handler時首先需要建立Looper的,不然報錯:RuntimeException: No Looper; Looper.prepare() wasn't called on this thread,而且每個執行緒下只需要建立一個Looper,不然會報錯:RuntimeException: Only one Looper may be created per thread。

class TestThread extends Thread{

        @Override
        public void run() {
            super.run();
            Looper.prepare();
            Handler handler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    //....
                }
            };
            Looper.loop();
        }
    }

但是UI執行緒是不需要建立的,是因為ActivityTread建立時就初始化了Looper,所以在UI主執行緒就能直接使用Handler。

在特定的執行緒中Handler使用當前的Looper來構建訊息迴圈系統,那handler是如何獲取到當前執行緒的Looper的?是通過ThreadLocal來獲取每個執行緒的Looper的,ThreadLocal是一個執行緒內部的資料儲存類。它是一個泛型類,裡面有set()和get()兩個主要方法。有關ThreadLocal的瞭解可參考Blog:ThreadLocal

      Handler通過send Message或者post去將訊息傳送到messageQueue中,會呼叫enqueueMessage()方法,而Message Queue的next()方法會將該訊息返回給Looper,Looper接收到訊息,就會處理--Looper就交由handler的dispatchMessage方法

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

接著,handler的dispatchMessage方法最終會呼叫handlerMessage方法來處理訊息:
public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

最後,就是我們經常見到的重寫handlerMessage方法去處理訊息及UI操作了。