[Android] Handler源碼解析 (Java層)

以前寫過一篇文章,概述了Android應用程序消息處理機制。本文在此文基礎上,在源碼級別上展開進行概述html

簡單用例

Handler的使用方法以下所示:android

Handler myHandler = new Handler() {  
          public void handleMessage(Message msg) {   
               switch (msg.what) {   
                    ...  
               }
          }   
     };

class myThread implements Runnable {   
          public void run() {  
               while (!Thread.currentThread().isInterrupted()) {    
                       
                    Message message = Message.obtain();   
                    message.what = TestHandler.GUIUPDATEIDENTIFIER;
                    TestHandler.this.myHandler.sendMessage(message);   
                    message.recycle();
                    try {   
                         Thread.sleep(100);    
                    } catch (InterruptedException e) {   
                         Thread.currentThread().interrupt();   
                    }   
               }   
          }   
     }

或者:框架

mHandler=new Handler();
mHandler.post(new Runnable(){

    void run(){
       ...
     }
});

又或者:async

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }
}

源碼解析

首先看其構造函數:ide

new Handler()
...
public Handler() {
    this(null, false);
}
...
public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
}
...
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) { // 默認爲false,若爲true則會檢測當前handler是不是靜態類
        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());
        }
    }
    // 1. 得到當前線程的looper
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    //2. 得到looper上的message queue
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

由此引入了兩個關鍵對象Looper和MessageQueue。函數

先看 mLooper = Looper.myLooper(); 這一句發生了什麼:oop

public static Looper myLooper() {
    return sThreadLocal.get();
}

能夠看到,該方法返回一個sThreadLocal對象中保存的Looper。關於ThreadLocal類,請參考這裏,本文不展開。
若是還沒有在當前線程上運行過Looper.prepare()的話,myLooper會返回null。接下來看看Looper.prepare()的實現:post

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));
}

能夠看到該方法只是簡單地新建了一個Looper對象,並將其保存在sThreadLocal中。接下來看一下Looper的構造函數。ui

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

調用完Looper.prepare()後,需調用Looper.loop()才能使消息循環運做起來,其源碼以下所示:this

public static void loop() {
    final Looper me = myLooper(); //1. 取出looper對象
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue; //2. 取出looper綁定的message queue

    // 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();
    // loop time.
    long tm = 0;
    ...
    for (;;) {
        Message msg = queue.next(); // 3. 堵塞式在message queue中取數據
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        ...
        msg.target.dispatchMessage(msg); 4. 分發message到指定的target handler

        ...

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            ...
        }

        msg.recycleUnchecked(); // 5. 回收message對象
        ...
    }
}

能夠簡單地將Looper.loop()理解成一個不斷檢測message queue是否有數據,如有即取出並執行回調的死循環。 接下來看一下Message類:

public final class Message implements Parcelable {

    public int what;

    public int arg1;

    public int arg2;

    public Object obj;

    ...

    /*package*/ int flags;

    /*package*/ long when;

    /*package*/ Bundle data;

    /*package*/ Handler target;

    /*package*/ Runnable callback;

    /*package*/ Message next;

    private static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;
}

what、arg一、arg2這些屬性本文不做介紹,咱們把目光集中在next、sPoolSync、sPool、sPoolSize這四個靜態屬性上。

當咱們調用Message.obtain()時,返回了一個Message對象。Message對象使用完畢後,調用recycle()方法將其回收。其中obtain方法的代碼以下所示:

public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

能夠看到,obtain方法被調用時,首先檢測sPool對象是否爲空,若不然將其當作新的message對象返回,並「指向"message對象的next屬性,sPoolSize自減。能夠看出message對象經過next屬性串成了一個鏈表,sPool爲「頭指針」。再來看看recycle方法的實現:

public void recycle() {
    if (isInUse()) {
        if (gCheckRecycle) {
            throw new IllegalStateException("This message cannot be recycled because it is still in use.");
        }
        return;
    }
    recycleUnchecked();
}

void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    // Clear out all other details.
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;

    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

若是message對象不是處於正在被使用的狀態,則會被回收。其屬性所有恢復到原始狀態後,放在了鏈表的頭部。sPool對象「指向」它,sPoolSize自增。

綜上能夠看出,經過obtain和recycle方法能夠重用message對象。經過操做next、sPoolSync、sPool、sPoolSize這四個屬性,實現了一個相似棧的對象池。

msg.target爲handler類型,即向handler成員的dispatchMessage方法傳入msg參數,其實現以下所示:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

這裏能夠看到回調了各類接口。

到目前爲止,咱們知道了如何處理在消息隊列裏面的msg對象,但仍不知道msg對象是如何放到消息隊列裏面的。一般來講,咱們經過Handler的sendMessage(msg)方法來發送消息,其源碼以下所示:

public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}
...
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
...
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);
}
...
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

可知sendMessage最終會調用queue.enqueueMessage(msg, uptimeMillis)將msg對象保存至message queue中,uptimeMillis表示msg執行回調的時刻。 咱們來看一下MessageQueue類的enqueueMessage方法:

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w("MessageQueue", e.getMessage(), e);
            msg.recycle();
            return false;
        }

        // 1.設置當前msg的狀態
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;

        // 2.檢測當前頭指針是否爲空(隊列爲空)或者沒有設置when 或者設置的when比頭指針的when要前
        if (p == null || when == 0 || when < p.when) {

            // 3. 插入隊列頭部,而且喚醒線程處理msg
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            
            //4. 幾種狀況要喚醒線程處理消息:1)隊列是堵塞的 2)barrier,頭部結點無target 3)當前msg是堵塞的
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }

            // 5. 將當前msg插入第一個比其when值大的結點前。
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

結合註釋,咱們能夠了解到msg push到queue中時,queue的狀態的變化和處理隊列的邏輯。

前文中Looper對象的loop方法中:

for (;;) {
    ...
    Message msg = queue.next(); // 3. 堵塞式在message queue中取數據
    if (msg == null) {
        // No message indicates that the message queue is quitting.
        return;
    }
    ...
    msg.target.dispatchMessage(msg); 4. 分發message到指定的target handler
    
    ...
}

能夠看出,message queue的next方法被調用時,可能會發生堵塞。咱們來看一看message queue的next方法:

Message next() {
    // 1. 判斷當前loop是否已經使用過,下文會解釋這個mPtr
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }

    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    
    // 2. 進入死循環,直到獲取到合法的msg對象爲止。
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands(); // 這個是什麼?
        }
        
        // 3. 進入等待,nextPollTimeoutMillis爲等待超時值
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // 4. 獲取下一個msg
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                
                // 當前節點爲barrier,因此要找到第一個asynchronous節點
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    
                    // 當前隊列裏最先的節點比當前時間還要晚,因此要進入堵塞狀態,超時值爲nextPollTimeoutMillis
                    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 (false) Log.v("MessageQueue", "Returning message: " + msg);
                    return msg;
                }
            } else {
                // 頭結點指向null
                nextPollTimeoutMillis = -1;
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }

            // 5. 若是當前狀態爲idle(就緒),則進入idle handle的代碼塊
            //    進入idle的狀況有:隊列爲空;隊列頭元素blocking;
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                // 6. 本輪喚醒(next被調用)時沒處理任何東西,故再次進入等待。
                mBlocked = true;
                continue;
            }

            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // 在一次next調用中,這個代碼塊只會執行一次
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler

            boolean keep = false;
            try {
                // 若是返回true,則idler被保留,下次next的idle時會被調用。
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf("MessageQueue", "IdleHandler threw exception", t);
            }

            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }

        // Reset the idle handler count to 0 so we do not run them again.
        pendingIdleHandlerCount = 0;

        // While calling an idle handler, a new message could have been delivered
        // so go back and look again for a pending message without waiting.
        nextPollTimeoutMillis = 0;
    }
}

代碼執行流程見註釋。其中IdleHandler是一個接口:

public static interface IdleHandler {
    boolean queueIdle();
}

IdleHandler提供了一個在MessageQueue進入idle時的一個hook point。更多時與barrier機制一塊兒使用,使message queue遇到barrier時產生一個回調。

總結

前面涉及到的幾個主要的類Handler、Looper、MessageQueue和Message的關係以下所述:

  1. Handler負責將Looper綁定到線程,初始化Looper和提供對外API。

  2. Looper負責消息循環和操做MessageQueue對象。

  3. MessageQueue實現了一個堵塞隊列。

  4. Message是一次業務中全部參數的載體。

框架圖以下所示:

+------------------+
            |      Handler     |
            +----+--------^----+
                 |        |
           send  |        |  dispatch
                 |        |
                 v        |
                +----- <---+
                |          |
                |  Looper  |
                |          |
                |          |
                +---> -----+
                  |      ^
           enqueue|      | next
                  |      |
         +--------v------+----------+
         |       MessageQueue       |
         +--------+------^----------+
                  |      |
  nativePollOnce  |      |   nativeWake
                  |      |
+-----------------v------+---------------------+
                Lower Layer

最後,留意到MessageQueue中有4個native方法:

// 初始化和銷燬
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
// 等待和喚醒
private native static void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
// 判斷native層的狀態
private native static boolean nativeIsIdling(long ptr);

將會在後續文章中進行介紹。

相關文章
相關標籤/搜索