天天看點

Looper Handler MessageQueue分

Looper Handler MessageQueue分析

我所有的文章都會送出到github上,喜歡的同學可以來github關注。歡迎送出你們的文章

Github https://github.com/twolight/LearnNote.git

郵箱 [email protected]

背景

在Android,線程之間的通信,通過Hanlder發送消息,具體Hanlder是向那個線程發送消息,是根據Hanlder在哪個線程中建立的。這同時決定了Hanlder的handleMessage方法運作在哪個線程。

public class MyActivity extends Activity{
        public void onCreate(...){
            //在主線程中建立,Handler發送的消息是向主線程發送,主線程處理消息
            Handler hanlder = new Hanlder();

        }
    }
    Thread thread = new Thread(new Runnable(){
        public void run(){
            //建立Looper
            Looper.prepare();
            //在子線程中建立,Handler發送的消息是向子線程發送,子線程處理消息
            Handler hanlder = new Hanlder();
            //Looper開始循環,從消息隊列中開始取出消息處理
            Looper.loop();
        }
    });
           

了解清楚之後,再來分析消息是如何發送到線程的,線上程中又是如何如何處理消息的。

分析

在上面的代碼中,之是以在Activity建立Hanlder之前沒有調用Looper.prepare()和Looper.loop(),是因為主線程在啟動時,已經自己建立Looper,并調用了loop。

是以我們知道,線程之間的通信,要經曆着幾個步驟。

  1. 建立Looper
  2. 建立Hanlder
  3. Looper開始循環(如果此時并沒有消息隊列并沒有消息,就線程就阻塞,直到有消息)
  4. Hanlder發送消息到消息隊列
  5. Looper開始處理消息(調用handler的dispatchMessage方法)

下面分析這幾個步驟

步驟分析

  • 建立Looper

    public static void prepare() {
        prepare(true);
    }
    
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException(...);
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    
    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    
     /**
     這是Andorid主線程,自己調用的,我們不應該在代碼中調用
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException(...);
            }
            sMainLooper = myLooper();
        }
    }
               
    涉及到ThreadLocal的概念,如果想要深入了解ThreadLocal機制,可以自己去了解下,這裡簡要說下。

    Implements a thread-local storage, that is, a variable for which each thread

    has its own value. All threads share the same {@code ThreadLocal} object,

    but each sees a different value when accessing it, and changes made by one

    thread do not affect the other threads. The implementation supports

    {@code null} values.

    ThreadLocal實作線程的本地變量存儲。通俗的來講,在一個Thread中ThreadLocal.set(變量),隻有這個線程能通過ThreadLocal.get拿出來這個變量。

    Looper.prepare()的作用是,建立一個Looper對象,并将它放到目前線程的ThreadLocal中去。并且建立一個MessageQueue.

  • 建立Hanlder

    public Handler(Callback callback, boolean async) {
        ...
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            ...
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
               

    由于建立Handler和建立Looper的線程是同一個線程,Handler能拿到之前建立的Looper對象,以及在Looper中建立的消息隊列。

    是以這就是Threadlocal的作用,隻要在同一個線程内,對于不同的對象,都能通過Threadlocal拿到共享的變量。Looper,就是這樣的共享的變量。

  • Looper消息循環

    關鍵代碼
    public static void loop() {
        final Looper me = myLooper();
        ...
        final MessageQueue queue = me.mQueue;
    
        ...
    
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
           ...
    
            msg.target.dispatchMessage(msg);
    
           ...
        }
        //next 方法代碼多,下面簡化成僞代碼
        Message next(){
            for(;;){
                if(有消息){
                    return msg;
                }else{
    
                }
                if(需要退出){
                    return null; 
                }
            }
    
        }
    }
               

    next()方法内是一個死循環,如果消息隊列沒有消息,那麼該方法不會傳回Message對象,代碼一直在其内部執行。如果MessageQueue正在退出,那麼傳回null。

    loop方法,在死循環中,不停的從消息隊列取消息,沒有消息,代碼就一直在next方法中執行,不會有傳回值。代碼不會往下執行。next()如果傳回null,表示消息隊列正在退出,那麼Looper也需要退出,就跳出死循環停止對消息隊列取消息。next()傳回消息,那麼調用的msg.target.dispatchMessage(msg),也就是調用Handler的dispatchMessage方法,去分發msg。

  • Hanlder發送消息

    Handler發送消息,最終會調用一下代碼
    public class Handler{
        ...
        private boolean enqueueMessage(MessageQueue queue, Message msg, long 
                    uptimeMillis) {
            //是以知道msg的target,就是發送這個消息的Handler。
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            //将消息方法消息隊列中
            return queue.enqueueMessage(msg, uptimeMillis);
        }
        ...
    }
               
    queue.enqueueMessage(msg, uptimeMillis);将消息方法消息隊列中,之前說的消息隊列的next方法,會一個死循環,不停地檢測是否有消息。隻要成功執行了queue.enqueueMessage,那麼Looper就能拿到發送的消息,并轉發消息。
  • 消息處理
    public class Handler{
        /**
        * Subclasses must implement this to receive messages.
        */
        //
        public void handleMessage(Message 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);
            }
        }
    }
               
    上面的步驟,說了Looper會調用msg.target.dispatchMessage(msg);讓Handler轉發消息,handler會調用handleMessage方法,這是一個空實作,我們可以在子類或者匿名類中重寫此方法。

總結

至此,線程間的消息機制,調用流程,就分析完了。