源碼解析-Handler機制

handler 是android SDK 提供給開發者方便進行異步消息處理的類,而我們熟悉的AsyncTack、retrofit內部都是用了handler,加以巧妙的封裝。由此看來handler似乎比我們想象的更重要。

進入正題,講解分爲三部分:機制說明、源碼分析、總結。

一: handler的機制說明

根據上圖我們理下邏輯:

首先主線程中會自動創建一個looper,而looper構造方法中會創建MessageQueue。而我們在主線程創建Handler的時候會取出當前線程中的looper。然後通過looper不斷的讀MessageQueue。looper讀到Message消息後就會回調handler的disapatchMessage方法。而handler發送消息就是在MessageQueue中添加Message。

 

二、源碼解析

2.1Handler的構造函數:

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

1)我們可以看到Handler的構造函數中,有個looper的創建。Looper.myLooper()是獲取當前線程的Looper。也就是說要使用Handler就必須當前線程有Looper對象。

2)mQueue即MessageQueue的實體類,我們可以看到mQueue是在looper內的。賦值給mQueue就是爲了looper和Handler共用一個消息隊列。

而Handler 中的sendEmptyMessage之類的方法最終都會走到enqueueMessage方法中,讓我們來看下源碼:

2.2Handler的enqueueMessage:

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

1)msg.target = this;中this指的是Handler本身。

2)queue.enqueueMessage(msg, uptimeMillis);既是將msg加入到MessageQueue的列表隊列中。

MessageQueue中的enqueueMessage(msg, uptimeMillis)即插入Message的方法,還有next()即讀取並刪除Message方法。這兩個方法是MessageQueue的最核心的兩個方法。

現在我們知道Handler是怎麼發生消息到達MessageQueue中,那麼MessageQueue中的Message又是怎麼被提取出來? 是通過Lopper的管理,那麼現在我們來看看來Lopper的源碼:

2.3Looper的prepare(boolean quitAllowed):

    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(boolean quitAllowed)這個方法是looper使用前必須調用的方法,由此代碼可以看出Lopper是通過sThreadLocal去添加管理looper。通過sThreadLocal 保存的話可以確保我們每個線程獲取的looper都是唯一的。sThreadLocal在很多地方都叫做線程本地變量,他能爲每個變量在每個線程都創建一個副本,那麼每個線程都可以訪問自己內部副本的變量。這樣就不會造成線程數據不一致的現象。

這個方法首先是判斷sThreadLocal是否爲空,不爲空即表示已創建好不必再做操作。若爲空則通過sThreadLocal的set方法創建好這個線程的looper對象放到容器當中。也就是說prepare方法就相當於是Looper的創建方法。

2.4Looper的myLooper()方法:

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

myLooper方法在Handler的構造方法中調用了,我們看源碼其實很簡單,直接在sThreadLocal中獲取當前線程中的Looper。而sThreadLocal中的Looper就是上面prepare方法中添加進去的。

ThreadLocal這個容器是線程所持有的,所以能夠保證我們的looper是和其他的線程獨立開來的。我們用get方法就可以獲得相應的Looper對象。

2.5Looper的構造方法:

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

1)在創建looper 對象的時候會創建MessageQueue對象。

看完這裏我們可以知道MessageQueue是愛Looper中的變量,而Message的target變量會指向Handler,從而使得MessageQueue、Looper和Handler三者關聯。

那麼Looper是怎麼去管理輪詢MessageQueue的呢,下面我們來看Looper的loop方法:

2.6Looper的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;

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

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycle();
        }
    }

我們可以看到loop也是通過myLooper()方法獲取當前線程的looper。然後判斷是否爲空,我們看如果爲空,會提示先調用Looper.prepare()方法。

判斷完後給MessageQueue賦值。

而後有一個死循環,不斷地從MessageQueue中獲取消息,如果消息不爲空則調用到msg.target.dispatchMessage(msg);這個方法,我們在看Handler源碼的時候看到message.target這個變量是指向Handler。也就是說調用了handler的dispatchMessage方法。

那麼我們就需要看下Handler的這個dispatchMessage源碼是怎麼處理的。

2.6Handler的dispathchMessage方法 :

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

    private static void handleCallback(Message message) {
        message.callback.run();
    }

    public interface Callback {
        public boolean handleMessage(Message msg);
    }

從源碼可以看到,msg先是判斷callback是否爲空,那麼callback是什麼?callback就是Runnable,如果不爲空,那麼就會調用handleCallback方法去執行Runnable的run方法。如果爲空就會去回調一個接口方法handleMessage。這個就是我們在寫Handler 的時候去重寫的handleMessage的方法。

那麼從源碼上已經查看完了handler的發送消息到Looper的MessageQueue輪詢。覺得需要重新理下邏輯的可以重新看下最上面的 handler機制說明。

三:總結

looper:

  1. 每個線程只有一個looper。(looper實體 會存儲在Looper的ThreadLocal的實體變量中,通過Looper的myLooper() 方法獲取)
  2. 負責不斷地讀取MessageQueue隊列裏的消息 (Looper的 loop方法)
  3.  構造方法中會創建MessageQueue(Looper的prepare()方法中會創建Looper)

 

MessageQueue:

  1. 存放Message的對象(Message可放Runnable或其他字符信息)
  2. 先進先出的方法管理 
  3. MessageQueue是使用單列表存儲Message

 

Handler:

  1. 發送消息(所有發送消息的方法最終都會調用enqueueMessage方法,將msg放入到MessageQueue的列表中)
  2. 處理消息(Handler 的dispatchMessage方法,如果是有 runnable就會執行,沒有的話就交到Handler的handleMessage方法中,一般我就是重新這個方法去處理 message)
  3. Handler需要正常工作需要有個looper對象(平時我們在主線程中不需要處理loop的準備,因爲主線程會自動創建looper。但是如果我們另起一個線程做異步操作的時候,直接用handler會報沒有loop的錯誤,我們需要在這個線程中先創建looper)

 

另外我們在子線程中使用Handler的時候必須自己調用Looper.prepare()和Looper.loop(),前者用於創建Looper,後者是啓動消息循環的查詢。

而Android也提供了HandlerThread類來供子線程使用Handler而不用自己再調用Looper的方法,HandlerThread其實就是將Looper的調用封裝在內部。讓開發者不用重複編寫調用Looper的方法。

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