Handler到底是如何完成線程切換的?

Handler到底是如何完成線程切換的?這個問題要從Handler最初的用法和原理講起。

首先我們列出正常情況下一個Handler使用的步驟然後講解分析如何實現,這裏不對一些基礎的概念做解釋,具體的請查閱源碼。

Handler的使用步驟

1.調用Looper.prepare();

2.創建Handler對象;

3.調用Looper.Loop()方法。

4.線程中發送消息。

這樣我們就創建好了一個Handler可以採用sendMessage等方法發送消息。

那麼這幾步都幹了什麼?

第一步:調用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));
}

創建了一個Looper對象保存在了一個ThreadLocal對象中,至於ThreadLocal是幹什麼的,查詢有關ThreadLocal如何存儲線程中數據的資料

上面代碼中創建了一個Loper對象,new Looper的時候我們可以看到:

private Looper(boolean quitAllowed) {
      mQueue = new MessageQueue(quitAllowed);
      mThread = Thread.currentThread();
 }
// 創建了一個MessageQueue對象,這個就是一個消息隊列用來保存我們發送的Message,其實這個MessageQUueue是一個單鏈表。

後邊我們會知道這裏創建了一個MessageQueue對象,從字面上來看就是一個消息隊列,其實他是一個鏈表,鏈表的數據結構就是便於插入和刪除。

第二步創建Handler

public Handler(Callback callback, boolean async) {
        ......
        mLooper = Looper.myLooper();
        ......
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

從構造方法中我們知道了Hanlder中獲取了一個Looper對象,這個Looper中保存了一個mQueue.這個Lopper對象就是我們前面保存在ThreadLocal中的那個。

第三步調用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;
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        for (;;) {// 無限循環
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ......
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {// 處理消息
                msg.target.dispatchMessage(msg);
            ......
    }

我們清楚的看到:loop()方法中獲取了Looper對象,取出了消息隊列mQueue,然後開始循環。同時調用了MessageQueue的next()方法取出一個消息最後調用了這個消息的dispathchMessage();看完源碼我們知道這個msg.tagget.dispatchMessage()就是Handler中的消息處理的方法。

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

這個方法的代碼中我們看到,首先判斷了callback是不是null.這是因爲Handler使用有兩種方法,一種可以通過創建CallBack的方式。最後調用了Hanlder的handleMessage(msg);這個方法就是我們經常自己實現的消息處理方法。所以我們就到達了目標。

第四步:發送消息

調用sendMessage()等方法發送消息,追蹤發送消息的方法,最後調用了以下的方法:

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

這裏拿到了我們前面Handler構造方法保存的mQueue,然後調用了enqueueMessage()方法。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

最後看queue.enqueueMessage(msg, uptimeMillis);在queue中處理了消息。

boolean enqueueMessage(Message msg, long when) {
       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 {
                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;
            }
        }
        return true;
    }

也就是通過消息要執行的時間,最後把消息插入鏈表中。

這樣我們就完成了消息的發送!

此刻:主線程的loop()方法一直在循環處理着消息,我們發送的消息就會到loop()方法中去,最後交給Handler處理。

那麼到底是怎麼樣就完成了線程的切換呢?

其實就是這個消息發送的過程,我們在不同的線程發送消息,線程之間的資源是共享的。也就是任何變量在任何線程都可以修改,只要做併發操作就好了。上述代碼中插入隊列就是加鎖的synchronized,Handler中我們使用的是同一個MessageQueue對象,同一時間只能一個線程對消息進行入隊操作。消息存儲到隊列中後,主線程的Looper還在一直循環loop()處理。這樣主線程就能拿到子線程存儲的Message對象,在我們沒有看見的時候完成了線程的切換。

所以總結來講就是:

1.創建了一個Looper對象保存在ThreadLocal中。這個Looper同時持有一個MessageQueue對象。

2.創建Handler獲取到Looper對象和MessageQueue對象。在調用sendMessage方法的時候在不同的線程(子線程)中把消息插入MessageQueue隊列。

3.在主線程中(UI線程),調用Looper的loop()方法無限循環查詢MessageQueue隊列是否有消息保存了。有消息就取出來調用dispatchMessage()方法處理。這個方法最終調用了我們自己重寫了消息處理方法handleMessage(msg);這樣就完成消息從子線程到主線程的無聲切換。

最後補充:我們經常使用Handler沒有創建Looper調用Looper.pepare()和Looper.loop()是因爲在ActivityThread中已經創建了主線程的Looper對象,保存在了ThreadLocal中,我們在創建Hander的時候就會從ThreadLocal中取出來這個Looper。

從主線程類ActivityThread的入口方法main()方法中我們可以清楚的看到系統幫我們如何實現的:

  public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();// 調用了prepareMainLooper(),創建Looper和MessageQueue

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();// 獲取Handler
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();// 開始調用loop()方法

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

 

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