Handler消息機制源碼解析(二)

上一篇Handler消息機制(一)爲大家講述了Handler是如何創建的這篇Handler消息機制(二)將爲大家介紹Handler是如何發送消息的。
一般使用的Handler方法

Handler.post(Runnable)
Handler.sendEmptyMessage()
Handler.sendMessage()
Handler.sendMessageDelayed()

這四種方法是大家在App中經常使用的那麼我們就首先看一下後面三種sendMessage()的方法
第一,Hander.sendEmptyMessage()看一下源碼

 public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }

那麼sendEmptyMessageDelayed(what, 0)的源碼是什麼呢?接着往下看

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

那麼調用的是sendMessageDelayed(msg, delayMillis)這個方法,OK,第二種常調用的方法和第四種其實是一樣的只不過進行了一個封裝。
那麼我們再來第三種調用的方法的背後
Handler.sendMessage()方法的源碼

 public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

OK,大家其實已經明白後面三種發送消息的方法最後調用的都是sendMessageDelayed(msg, 0).這個方法。那讓我們來看看這個方法到底是何方神聖,請看一下這個方法的源碼。

 public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

最後調用的是sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)這個方法。這個方法有兩個參數,一個是需要發送的消息。另一個是發送消息的時間,這個時間是系統當前的時間加上設置的延遲時間。明白了,那麼我們來看看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);
    }

在這個方法中首先涉及了一個類MessageQueue這個類是做什麼的,看類的名字就知道這是一個消息隊列。大家這個時候發現了一個對象mQueue,這個時候很多人就會好奇了這個mQueue對象是做什麼的?
從哪裏來,這個時候我們就要回到Handler消息機制(一)裏面實例化對象的時候實例化每個Handler必須要有Loop,那這個Loop到底起到了什麼作用?
我們重新看一下Handler()實例化對象時的構造方法

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

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

大家這個時候有沒有發現 mQueue = mLooper.mQueue,這個時候眼前一亮,mQueue這個參數是用Loop獲得的,那麼我們就來看看mLooper.mQueue到底是個什麼鬼?

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

大家這個時候突然發現mQueue是在創建Looper對象時就會創建一個與Looper相關聯的消息隊列,那問題就來了Looper對象又是在什麼時候創建的。這個時候我們不得不聯繫到上一篇博客Hander消息機制(一),在Hander實例化對象時,讓我們來回顧一下當時的源代碼,實例化對象的時候如果不是在主線程中必須使用Loop.prepare()

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

一切清楚了,在Loop.prepare()這個方法的時候就實例化了一個Looper對象,然後在實例化Looper對象的時候又實例化了一個與這個Looper相關聯的MessageQueue(消息隊列),來理一下這個邏輯,意思就是每一個Looper都有一個與之相關的消息隊列,而Looper又是與什麼相關呢。在前面的ThreadLocal中介紹的就明白了,每一個線程對應一個與之相關的Looper。我們已經弄清楚了這個mQueue,現在重新回到發送消息時的方法

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

這個方法裏唯一要注意的就是 msg.target = this,這個表明其實msg.target其實就是當前Handler的實例化對象,當然這裏也許用不到,到下面怎麼接收這個消息將用到,大家發現最後調用的是queue.enqueueMessage(msg, uptimeMillis)這個方法,來看看這個方法的源代碼

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(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            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 {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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;
                    }
                }
                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;
    }

這裏大家需要理解清楚一個邏輯,就是消息進入消息隊列是怎樣進入的,大家可以仔細注意一下這段代碼, 大家從20行代碼往下看。這邊的邏輯首先是msg.when = when;Message p = mMessages這兩句
,when自然就是傳進來的參數,那麼mMessages是啥,大家其實這就是個消息的全局變量,連實例化這個對象也沒,那麼如果是第一次必然是p == null ,走入 if (p == null || when == 0 || when < p.when)代碼中,那這個時候會做什麼動作,msg.next = p;mMessages = msg;會把下一個消息變成p,mMessages可以理解爲當前傳進來的消息。這個時候如果再進來一個消息,是進行怎樣的判斷 if (p == null || when == 0 || when < p.when)這個裏面還有兩個參數,第一是when ==0, 第二是when< p.when ,when其實就是消息當前消息的when,但是這個時候的p已經變成上一次傳進來的消息,Message p = mMessages, 其實這個判斷就是說如果穿進來的消息的when爲0,或者when小於新進來的消息的when.就把這個上一個消息放到下一個消息。OK,理一下這個邏輯,如果msg.when越小就越在消息隊列的前面,when就是傳進來的系統時間和設置的延遲時間之和。

OK,我們開始講述最後一個方法Handler.post(Runnable),這裏我們首先要將一個預備知識點。很多安卓程序有這個誤區,就是實現一個Runnable接口到底是不是一個線程,那我們來做一個試驗,請看試驗代碼。

public class Test {

    public static void main(String[] args) {
        Test03 test03 = new Test03();
        test03.run();
        Thread thread = Thread.currentThread();
        System.out.println("TestName =" + thread.getId() );
    }
}
public class Test03 implements Runnable{

    @Override
    public void run() {
        // TODO Auto-generated method stub
        Thread thread = Thread.currentThread();

        System.out.println("Test03Id ="  +thread.getId());

    }

}

打印結果:

Test03Id =1
TestName =1

這個打印結果的線程Id是一樣的,那麼這就證明一個問題,一個類實現一個Runable接口並不是說這個類變成單獨的一個線程,實際上只是實現了一個接口。那麼接下來接着來看Handler.post(Runnable)這個的源碼

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

這個最終調用的是sendMessageDelayed()方法,然後sendMessageDelayed()方法最終又是調用的sendMessageAtTime(),這是說通過

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

這段代碼其實就是告訴我們把Runnable接口變成一個消息塞了進去,然後給這個消息一個callback。OK,所有發消息的基本已經講述完畢。至於這三種方法怎麼回調,請看下一篇博客Handler消息機制源碼解析(三)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章