再談Handler、Looper、Message、MessageQueue基礎流程分析

anAngryAnt發表於2018-02-06

老司機們都知道,Android的執行緒間通訊就靠Handler、Looper、Message、MessageQueue這四個麻瓜兄弟了,那麼,他們是怎麼運作的呢?下面我們來分析一下

Looper(先分析這個是因為能夠引出四者的關係)

在Looper中,維持一個Thread物件以及MessageQueue,通過Looper的建構函式我們可以知道:

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//傳入的引數代表這個Queue是否能夠被退出
        mThread = Thread.currentThread();
    }
複製程式碼

Looper在建構函式裡幹了兩件事情:

  1. 將執行緒物件指向了建立Looper的執行緒
  2. 建立了一個新的MessageQueue

分析完建構函式之後,接下來我們主要分析兩個方法:

  1. looper.loop()
  2. looper.prepare()

looper.loop()(在當前執行緒啟動一個Message loop機制,此段程式碼將直接分析出Looper、Handler、Message、MessageQueue的關係)

 public static void loop() {
        final Looper me = myLooper();//獲得當前執行緒繫結的Looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;//獲得與Looper繫結的MessageQueue

        // 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();
        
        //進入死迴圈,不斷地去取物件,分發物件到Handler中消費
        for (;;) {
            Message msg = queue.next(); // 不斷的取下一個Message物件,在這裡可能會造成堵塞。
            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);
            }
            
            //在這裡,開始分發Message了
            //至於這個target是神馬?什麼時候被賦值的? 
            //我們一會分析Handler的時候就會講到
            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);
            }
            
            //當分發完Message之後,當然要標記將該Message標記為 *正在使用* 啦
            msg.recycleUnchecked();
        }
    }
複製程式碼

分析了上面的原始碼,我們可以意識到,最重要的方法是:

  1. queue.next()
  2. msg.target.dispatchMessage(msg)
  3. msg.recycleUnchecked()

其實Looper中最重要的部分都是由MessageMessageQueue組成的有木有!這段最重要的程式碼中涉及到了四個物件,他們與彼此的關係如下:

  1. MessageQueue:裝食物的容器
  2. Message:被裝的食物
  3. Handler(msg.target實際上就是Handler):食物的消費者
  4. Looper:負責分發食物的人

looper.prepare()(在當前執行緒關聯一個Looper物件)

 private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //在當前執行緒繫結一個Looper
        sThreadLocal.set(new Looper(quitAllowed));
    }
複製程式碼

以上程式碼只做了兩件事情:

  1. 判斷當前執行緒有木有Looper,如果有則丟擲異常(在這裡我們就可以知道,Android規定一個執行緒只能夠擁有一個與自己關聯的Looper)。
  2. 如果沒有的話,那麼就設定一個新的Looper到當前執行緒。

Handler由於我們使用Handler的通常性的第一步是:

 Handler handler = new Handler(){
        //你們有沒有很好奇這個方法是在哪裡被回撥的?
        //我也是!所以接下來會分析到喲!
        @Override
        public void handleMessage(Message msg) {
            //Handler your Message
        }
    };
複製程式碼

所以我們先來分析Handler的構造方法

//空引數的構造方法與之對應,這裡只給出主要的程式碼,具體大家可以到原始碼中檢視
public Handler(Callback callback, boolean async) {
        //列印記憶體洩露提醒log
        ....
        
        //獲取與建立Handler執行緒繫結的Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //獲取與Looper繫結的MessageQueue
        //因為一個Looper就只有一個MessageQueue,也就是與當前執行緒繫結的MessageQueue
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
        
    }
複製程式碼

帶上問題:

  1. Looper.loop()死迴圈中的msg.target是什麼時候被賦值的?
  2. handler.handleMessage(msg)在什麼時候被回撥的?

A1:Looper.loop()死迴圈中的msg.target是什麼時候被賦值的?

要分析這個問題,很自然的我們想到從傳送訊息開始,無論是handler.sendMessage(msg)還是handler.sendEmptyMessage(what),我們最終都可以追溯到以下方法

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        //引用Handler中的MessageQueue
        //這個MessageQueue就是建立Looper時被建立的MessageQueue
        MessageQueue queue = mQueue;
        
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        //將新來的Message加入到MessageQueue中
        return enqueueMessage(queue, msg, 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);
    }
複製程式碼

A2:handler.handleMessage(msg)在什麼時候被回撥的?

通過以上的分析,我們很明確的知道Message中的target是在什麼時候被賦值的了,我們先來分析在Looper.loop()中出現過的過的dispatchMessage(msg)方法

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //看到這個大寫加粗的方法呼叫沒!
            **handleMessage(msg);**
        }
    }
複製程式碼

加上以上分析,我們將之前分析結果串起來,就可以知道了某些東西: Looper.loop()不斷地獲取MessageQueue中的Message,然後呼叫與Message繫結的Handler物件的dispatchMessage方法,最後,我們看到了handleMessage就在dispatchMessage方法裡被呼叫的。


通過以上的分析,我們可以很清晰的知道Handler、Looper、Message、MessageQueue這四者的關係以及如何合作的了。

總結:

當我們呼叫handler.sendMessage(msg)方法傳送一個Message時,實際上這個Message是傳送到與當前執行緒繫結的一個MessageQueue中,然後與當前執行緒繫結Looper將會不斷的從MessageQueue中取出新的Message,呼叫msg.target.dispathMessage(msg)方法將訊息分發到與Message繫結的handler.handleMessage()方法中。

一個Thread對應多個Handler 一個Thread對應一個LooperMessageQueueHandlerThread共享LooperMessageQueueMessage只是訊息的載體,將會被髮送到與執行緒繫結的唯一的MessageQueue中,並且被與執行緒繫結的唯一的Looper分發,被與其自身繫結的Handler消費。


  • Enjoy Android :) 如果有誤,輕噴,歡迎指正。

相關文章