深入理解Handler,MessageQueue,Looper 轉:http://www.jianshu.com/p/6d143b8c15ee 前言: 其實講 Handler 內部機制的博客已經很多了,但是自己還是要在看一遍,源碼是最好的資料。在具體看源碼之前,有必要先理解一下 Handler、Looper、MessageQueue 以及 Message 他們的關系。 Looper: 是一個消息輪訓器,他有一個叫 loop() 的方法,用于啟動一個循環,不停的去輪詢消息池 MessageQueue: 就是上面說到的消息池 Handler: 用于發送消息,和處理消息 Message: 一個消息對象
源碼分析開始: 一切要從Handler的構造函數開始講起
public Handler(Callback callback, boolean async) { mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.我們可以看到,Handler定義了一個MessageQueue對象mQueue和一個Looper對象mLooper。順著源碼繼續往下看,跳轉到Looper.myLooper()方法。static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();public static @Nullable Looper myLooper() { return sThreadLocal.get();}這里涉及了ThreadLocal,暫時不講。通過ThreadLocal的get方法獲取Looper并返回。那么問題來了?我們只看到了get,并沒有看到set。如果沒有set的話,get出來就會為null,通過Handler的構造函數我們知道,mLooper==null會拋出異常。而我們在使用Handler的過程中并沒有遇到該異常。那問題來了,到底在哪里進行了set呢?通過對Looper源碼搜索發現,改方法進行set操作:
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));}可以看到在prepare()方法中進行了set操作,那么問題又來了,哪里調用了該方法呢?因為prepare方法是私有方法,所以肯定是本類中調用,通過搜索發現以下方法調用了prepare()方法:
* 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()} */public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); }}那一切就是順利成章了。到這里,有人又會問,那這個方法又是誰調用的呢?看注釋發現,該方法在啟動app的時候就已經調用了。具體是在ActivityThread的main方法中啟動。 到這里為止,我們了解了Handler,Looper的初始化相關知識。接下來,我們需要了解的是如何進行發送和處理Message。 發送Message代碼如下:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis);}這個方法我們主要是看enqueueMessage():
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis);}這里關鍵的是看懂msg.target = this;msg就是Message的對象,那么看Message源碼發現,它的target屬性是Handler target;那么,為什么發送消息的時候需要將this(當前Handler對象)帶過去呢?咋們暫且繼續… 這個方法實際執行的還是queue.enqueueMessage(),我們找到MessageQueue類的相關方法,發現以下代碼 msg.next = p; // invariant: p == prev.next prev.next = msg; 通過這兩行代碼我們發現,MessageQueue并不是隊列,而是單鏈表。所以下次面試的時候,如果你支出handler的消息隊列其實是利用Message的單鏈表實現的肯定能加分的。 到此,發送消息已經講完了。下面我們看看處理消息是怎樣進行的呢?我們知道,Loope會從MessageQueue中不斷拿消息,我們看看Looper.loop()代碼:
public static void loop() { for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } msg.target.dispatchMessage(msg); msg.recycleUnchecked(); }}這里取出消息并分發之。是不是有頓悟的感覺,回到上面我們遺留的問題:msg.target = this,將this傳遞過去,言外之意就是哪個Handler發送的消息就由哪個Handler進行處理。那么我們來看看Handler的dispatchMessage 方法:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}終于見到我們最常見的handleMessage()了。他首先判斷 Message 對象的 callback 對象是不是為空,如果不為空,就直接調用 handleCallback 方法,并把 msg 對象傳遞過去,這樣消息就被處理了,我們來看 Message 的 handleCallback 方法
private static void handleCallback(Message message) { message.callback.run();}沒什么好說的了,直接調用 Handler post 的 Runnable 對象的 run() 方法。 如果在發送消息時,我們沒有給 Message 設置 callback 對象,那么程序會執行到 else 語句塊,此時首先判斷 Handler 的 mCallBack 對象是不是空的,如果不為空,直接調用 mCallback 的 handleMessage 方法進行消息處理。最終,只有當 Handler 的 mCallback 對象為空,才會執行自己的 handleMessage 方法。
新聞熱點
疑難解答