Android Handler機制之Handler 、MessageQueue 、Looper

很隨意.jpg

該文章屬於《Android Handler機制之》系列文章,若是想了解更多,請點擊 《Android Handler機制之總目錄》bash

前言

上篇文章,咱們講了ThreadLocal,瞭解了線程本地變量的實質,若是有小夥伴仍是不熟悉ThreadLocal原理的,請參看上篇文章《 Android Handler機制之ThreadLocal》。若是你已經閱讀 了該文章,那如今咱們就一塊兒來了解Handler與MessageQueue與Looper三者之間的關係及其內部原理。async

Handler、MessageQueue、Looper三者之間的關係

在瞭解其三者關係以前,我先給你們一個全局的關係圖,接下來的文章會根據該關係圖,進行相應的補充與描述。 ide

HandlerLooperMessage關係.png

從上圖中咱們能夠看出幾點函數

  • Handler的建立是與Looper建立的線程是相同的。
  • Looper中內部維護了一個MessageQueue(也就是消息隊列)。且該隊列是經過鏈表的形式實現的。
  • Hanlder最終經過sendMessage方法將消息發送到Looper中對應的MessageQueue中。
  • Looper經過消息循環獲取消息後,會調用對應的消息中的target(target對應的是發消息的Handler)的dispatchMessage()方法來處理消息。

Looper原理

由於消息隊列(MessageQueue的建立是在Looper中內部建立的,同時Handler消息的發送與處理都是圍繞着Looper來進行的,因此咱們首先來說Looper。oop

Looper是如何與主線程關聯的

在平時開發中,咱們使用Handler主要是爲了在主線程中去更新UI,那麼Looper是如何與主線程進行關聯的呢?在Android中,App進程是由Zygote fork 而建立的,而咱們的ActivityThread就是運行在該進程下的主線程中,那麼在ActivityThread的main方法中,Looper會經過prepareMainLooper()來建立內部的消息隊列(MessageQueue),同時會經過loop()構建消息循環。具體代碼以下圖所示:post

public static void main(String[] args) {
		...省略部分代碼
        Looper.prepareMainLooper();//
        
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
複製代碼

要了解當前Loooper如何與主線程進行關聯的,須要繼續查看prepareMainLooper()方法。下述代碼中,爲了你們方便,我將prepareMainLooper()方法所涉及到的方法所有羅列了出來。ui

//建立主線程Looper對象
   public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    
  //Looper與當前主線程綁定
  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));//建立Looper對象,放入主線程局部變量中
    }
    
  //獲取當前主線程的Looper對象
  public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
   }

複製代碼

觀察上訴代碼,咱們發現,prepareMainLooper方法內部調用prepare()方法(這裏咱們忽略該方法中的參數 quitAllowed),而prepare內部調用的是ThreadLocal的set()方法。若是你閱讀了以前我寫的《 Android Handler機制之ThreadLocal》,那麼你們應該知道了當前Looper對象已經與主線程關聯了(也能夠說,當前主線程中保存了當前Looper對象的引用)。this

Looper內部建立消息隊列

在瞭解了Looper對象怎麼與當前線程關聯的後,咱們來看看Looper類中的具體方法。以前咱們說過,在建立Looper對象的時候,當前Looper對象內部也會建立與之關聯的消息隊列(MessageQueue)。那麼查看Looper對應的構造函數:spa

final MessageQueue mQueue;
	//Looper內部會建立MessageQueue對象
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
複製代碼

從Looper對象的構造函數中,咱們很明顯的看出內部建立了MessageQueue對象,也驗證了咱們以前的說法。線程

Looper的消息循環

當前Looper對象與主線程關聯後,接着會調用Looper對象中的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;
		...省略部分代碼
        for (;;) {//一直循環去獲取消息隊列中的消息
            Message msg = queue.next(); //該方法可能堵塞,
            if (msg == null) {
	            //若是沒有消息,表示當前消息隊列已經退出
                return;
            }

        ...省略部分代碼
            try {
             //獲取消息後,執行發送消息的handler的dispatchMessage方法。
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
        ...省略部分代碼
            }

            msg.recycleUnchecked();
        }
    }
複製代碼

經過上述代碼,咱們能夠看出,在Looper中的loop()方法中會一直去拿當前消息隊列中的消息,若是能取出消息會調用該消息的target去執行dispatchMessage()方法。若是沒有消息,就直接退出消息循環。

MessageQueue原理

MessageQueue的next()方法

由於Looper中loop()方法會循環調用MessageQueue中的next方法,接下來帶着你們一塊兒查看該方法。代碼以下圖所示:

Message next() {
	...省略部分代碼
	 for (;;) {
	     synchronized (this) {
	     ...省略部分代碼
         if (msg != null) {
           if (now < msg.when) {
                nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
	                    //遍歷消息列表,取出消息
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                }
              ...省略部分代碼
        }
      }
    }
複製代碼

上述代碼中,我省略了不少代碼,如今你們不須要關心省略的內容,你們只要關心大的一個方向就夠了,關於MessageQueue的next()具體詳解,會在下篇文章《Android Handler機制之Message的發送與取出》具體介紹。好了,你們把狀態調整過來。 在上文中,咱們說過MessageQueue是以鏈表的形式來存儲消息的,從next()方法中咱們能分析出來,next()方法會一直從MessageQueue中去獲取消息,直到獲取消息後纔會退出。

MessageQueue的enqueueMessage()方法

經過上文,咱們已經瞭解Message取消息的流程,如今咱們來看看消息隊列的加入過程。

boolean enqueueMessage(Message msg, long when) {
	      ...省略部分代碼
        synchronized (this) {
          ...省略部分代碼
            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 {
              ...省略部分代碼
                //循環遍歷消息隊列,把當前進入的消息放入合適的位置(比較等待時間)
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                //將消息插入合適的位置
                msg.next = p; 
                prev.next = msg;
            }

	      ...省略部分代碼
        }
        return true;
    }
複製代碼

上訴代碼中,咱們把重心放在for循環中,在for循環中主要乾了 一件事,就是根據當前meesag.when的值,來肯定當前插入的消息應該放入消息隊列的位置。(當前小夥伴肯能會對message.when感到困惑,仍是那句話,現階段咱們只用關心主要的流程,具體的方法詳解會在下篇文章《Android Handler機制之Message的發送與取出》具體介紹)

Handler的原理

瞭解了Looper與MessageQueue的原理後,咱們大體瞭解了整個消息處理的關係,如今就剩下發消息與處理消息的流程了。最後一點了,你們堅持看完。

Handler是怎麼與Looper進行關聯的

在文章最開始的圖中,Handler發消息最終會發送到對應的Looper下的MessageQueue中。那麼也就是說Handler與Looper之間必然有關聯。那麼它是怎麼與Looper進行關聯的呢?查看Handler的構造函數:

//不帶Looper的構造函數
  public Handler() {this(null, false);}
  public Handler(boolean async) {this(null, async);}
  public Handler(Callback callback) {this(callback, false);}
  public Handler(boolean async) {this(null, async);}
  public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }
		//Looper.myLooper()內部會調用sThreadLocal.get(),獲取線程中保存的looper局部變量
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
	    //獲取當前Looper中的MessageQueue
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    
 //帶Looper參數的構造函數
  public Handler(Looper looper) { this(looper, null, false); }
  public Handler(Looper looper, Callback callback) { this(looper, callback, false);}
  public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        //獲取當前Looper中的MessageQueue
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }  
複製代碼

在Handler的構造函數中,主要分爲兩種類型的構造函數,一種是帶Looper參數的構造函數,一種是不帶Looper參數的構造函數。

  • 在不帶Looper參數的構造函數中,是經過Looper.myLooper()來獲取當前Looper對象的(也就是說,Handler獲取的Looper對象是與當前實例化當前Handler的線程相關的,那麼若是Handler對象是在主線程中建立的,那麼獲取的就是主線程的Looper,注意前提條件當前線程線程已經經過Looper.prepare()與Looper.loop()構建了循環消息隊列,由於只有調用了該方法後,纔會將當前Looper對象放入線程的局部變量中
  • 在帶Looper參數的構造函數中,Looper對象是經過外部直接傳入的。(這裏其實給咱們提供了一個思路,也就是咱們能夠構建本身的消息處理循環,具體細節參看類HandlerThread)

Handler怎麼將消息發送到MessaageQueue(消息隊列)中去

在瞭解Handler怎麼將消息發送到MessageQueue(消息隊列),咱們先來了解Handler的發消息的系列方法。

//發送及時消息
public final boolean sendMessage(Message msg)
public final boolean sendEmptyMessage(int what)
public final boolean post(Runnable r)

//發送延時消息
public final boolean sendEmptyMessageDelayed(int what, long delayMillis)
public final boolean sendMessageDelayed(Message msg, long delayMillis)
public final boolean postDelayed(Runnable r, long delayMillis)

//發送定時消息
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
public final boolean postAtTime(Runnable r, long uptimeMillis)
複製代碼

在Handler發消息的方法中,咱們能夠總共發消息的種類,分爲三種狀況,第一種是及時消息,第二種是發送延時消息,第三種是定時消息。其中關於消息怎麼在消息隊列中排列與處理。具體的方法詳解會在下篇文章《Android Handler機制之Message的發送與取出》具體介紹。

經過查看Handler發送消息的幾個方法。咱們發現內部都調用了MessageQueue的enqueueMessage()方法。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//設置message.target爲當前Handler對象
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);//獲取當前MessageQueue.將消息加入隊列中
    }
複製代碼

該方法內部其實很簡單,就是獲取當前MessageQueue對象,將消息將入消息隊列中去了。其中須要你們須要的注意的是這段代碼msg.target = this。該代碼意思就是當前的消息保存着當前發送消息的Handler對象的應用。該行代碼很是重要。由於最後涉及到消息的處理。

Handler怎麼處理消息

經過上文的描述,如今咱們已經大體瞭解Handler是怎麼將消息加入到消息隊列中去了,如今須要關心的是當前消息是怎麼被處理的。你們還記的以前咱們講過的Looper原理吧,Looper會調用loop()方法循環的取消息。當取出消息後會調用message.target.dispatchMessage(Message msg)方法。其中message.target從上文咱們已經知道了,就是當前發送消息的Handler。那麼最終也就會回到Handler中的dispatchMessage()方法。

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {//第一步,判斷msg.callback
            handleCallback(msg);
        } else {
            if (mCallback != null) {//第二步、判斷Handler的callBack
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);//第三步,執行Handler的handleMessage方法
        }
    }
複製代碼

觀察該方法,咱們能夠得出,Handler中的dispatchMessage()方法主要處理了三個步驟,下面分別對這三個步驟進行講解

第一步,執行message.callback

在Handler中的dispatchMessage()方法中,咱們已經知道若是msg.callback != null,那麼咱們會直接走handleCallback(msg)方法。在瞭解該方法以前,首先咱們要知道msg.callback對於的類是什麼。這裏我就直接給你們列出來了。其實msg.callback對應是如下四個方法的Runnable對象。

public final boolean post(Runnable r)
public final boolean postAtTime(Runnable r, long uptimeMillis)
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
public final boolean postDelayed(Runnable r, long delayMillis)
複製代碼

以上四個方法在發送Runnable對象時,都會調用getPostMessage(Runnable r) 方法,且該方法都會將Runnable封裝在Message對象的callback屬性上。具體以下getPostMessage(Runnable r) 方法所示:

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
複製代碼

在瞭解了Message的callback到底什麼事後,咱們再來看看handleCallback(Message message)方法

private static void handleCallback(Message message) {
        message.callback.run();
    }
複製代碼

該方法其實很簡單,就是調用相應Runnable的run()方法。

第二步,執行Handler的callBack

若是當前Message.callback爲空,接下來會判斷Handler中的Callback回調是否爲空,若是不爲空則執行Callback的handleMessage(Message msg)方法。Callback的具體聲明以下:

//避免建立Handler對象從新HandlerMessage方法,你能夠直接傳入Callback接口實現
  public interface Callback {
        public boolean handleMessage(Message msg);
    }
複製代碼

其中在Handler的幾個構造函數中,能夠傳入相應Callback接口實現。

public Handler(Callback callback) 
public Handler(Looper looper, Callback callback) 
public Handler(Callback callback, boolean async)
public Handler(Looper looper, Callback callback, boolean async)
複製代碼

第三步,執行Handler的handleMessage)

若是都不知足上面描述的第1、第二狀況時,會最終調用Handler的handleMessage(Message msg)方法。

//Handler內部該方法是空實現,須要子類具體實現
  public void handleMessage(Message msg) {  }
複製代碼

爲了方便你們記憶,我將Handler中的dispatchMessage()具體的邏輯流程畫了出來。你們按需觀看。

dispatchMessage步驟.png

最後

看到最後你們已經發現該篇文章主要着重於將Handler機制的整個流程,對於不少的代碼細節並無過多的描述,特別是關於Looper從MessageQueue(消息隊列)中取消息與MessageQueue(消息隊列)怎麼放入消息的具體細節。不用擔憂,關於這兩個知識點將會在下篇文章《Android Handler機制之Message的發送與取出》具體描述。

相關文章
相關標籤/搜索