Handler,Looper,MessageQueue的工作原理

功能劃分

Handler的使用必須與幾個組件一起。

*Message: Handler接收和處理的消息對象,類似於一個業務類,封裝了一些變量。
*MessageQueue:一個隊列容器,採用先進先出的原則管理Message。程序創建Looper對象的時候會在構造函數中創建MessageQueue對象。
*Looper:負責管理MessageQueue和Message對象,讀取到MessageQueue中的Message之後就會採用sendMessage的方式把消息發送給對應(發送該消息)的Handler來處理。

要在一個線程中使用Handler,則該線程中必須要有一個MessageQueue對象。而MessageQueue是由Looper來管理的。所以在一個線程中必須要有一個Looper對象,而且每個線程只能有一個Looper對象。

而根據線程的不同分類,Handler的使用就大致可以分爲兩種情況。

UI線程使用Handler

分析

在UI線程中無需創建Looper對象,因爲在應用啓動,UI線程啓動的時候 系統已經自動創建了一個Looper對象。

這裏就要提到一個類ActivityThread,這個類是程序啓動的入口
下面是該類的部分源碼

public final class ActivityThread {  
    ......  

    public static final void main(String[] args) {  
        ......  

        Looper.prepareMainLooper();  

        ......  

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

        ......  

        Looper.loop();  

        ......  

        thread.detach();  

        ......  
    }  
} 

其中可以看到該類中的main函數,這個函數就是進程的入口函數。在main函數中調用了 Looper.prepareMainLooper(); 就是在當前線程中創建Looper對象,所以在UI線程中我們是不需要 手動創建Looper的。

接下來再看Looper的源碼。

public final class Looper {
    private static final String TAG = "Looper";

    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;
    final Thread mThread;

    private Printer mLogging;

     /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    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));
    }

這裏面的public 修飾的prepare()方法會調用private的prepare方法,然後去檢查當前線程是否有Looper對象,沒有則創建,如果當前線程已經有了Looper對象,再次調用Looper.prepare()方法的話,會拋出異常的。
這裏的ThreadLocal可以把它理解成一個容器吧。關於該類的詳細信息,可以自己查看源碼。

總結

在UI線程中要使用Handler,只需要創建Handler對象,重寫Handler中的 handlerMessage方法,發送消息 即可。

子線程(非UI線程)使用Handler

分析

在子線程中使用Handler,我們必須手動爲當前線程創建一個Looper對象,調用Looper.prepare()方法即可。

創建完成之後記得啓動Looper的輪詢,調用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
            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();
        }
    }

loop方法可以看出,它會無線循環檢查MessageQueue。

總結

在子線程中使用Handler
1.需要手動爲當前線程創建Looper對象
2.創建Handler對象,重寫handlerMessage方法
3.調用Looper的loop方法啓動Looper

這裏只是簡單的介紹一下 三者之間的關係,與工作原理。Handler是系統通信的重要組件,它的用法遠不止這些,更深層次的用法。可以查看羅昇陽前輩的博文

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章