Android Handler 源碼及原理分析

參考資料:

ThreadLocal:https://www.cnblogs.com/coshaho/p/5127135.html

Handler:https://www.jianshu.com/p/3d8f7ec1017a


0.前緒

通過Handler發送的消息,會先存到一個消息列表中,就是MessageQueue這個消息列表裏。Looper這個類會循環不停地從這個消息列表中按順序取消息,取到消息後,進行消息處理(使用handleMessage方法)。很簡單一個原理,實現了消息的順序處理。

Handler涉及類包括:Handler.java、Looper.java、MessageQueue.java、ThreadLocal.java、ThreadLocalMap.java

1.源碼分析

1.創建Handler

handler翻譯中文是操作者,顧名思義,Handler的作用就是用來處理消息的。

Handler mHandler = new Handler();

Handler mHandler = new handler() {

    public void handleMessage(Message msg){
        // handle message
    }

};

最常見的兩種創建方式,我們常會在Activity中這樣創建使用,一般用於在主線程處理任務。

看構造方法的源碼,最後會跑這個方法:

Handler.java

    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 " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

第11行,通過 Looper.myLooper() 獲取到了Looper。

注意,此代碼高能,信息量巨大,下面,進入這行代碼!:

Looper.java

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

就一行代碼,你以爲我騙人?no,no,no。

這個方法是Looper類中的一個靜態方法,這行代碼中包含了重要角色: ThredLocal

這可是Looper線程綁定的精髓(嚴肅臉?)。

啥是線程綁定???

就是一個線程只有一個Looper對象

而用的消息隊列呢,是Looper的一個內部變量,所以,一個線程也只有一個消息隊列。

2.引入Looper

怎麼突然從Handler的話題轉移到了Looper?

我們之前說過,Looper會循環不停地從消息隊列裏按順序取消息。而這個消息隊列就是Looper的小弟(一個內部變量),拿到這個Looper,我們就可以往它的消息隊列裏放消息啦。

看看我們平時是怎麼用Handler發消息的:

Message msg = Message.obtain();
msg.what = args1;
msg.obj = args2;
mHander.sendMessage(msg);

其實這個sendMessage方法就是起到把消息扔給消息隊列的作用。這個Looper很重要吧。

先看看Looper代碼中的 sThreadLocal在哪定義的

Looper.java
 
   static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

泛型參數是Looper的一個ThreadLocal靜態對象。

回去看 sThreadLocal.get() 方法,這是ThreadLocal的一個方法

ThreadLocal.java
    
public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

分析一波上面代碼:

先獲取到了當前運行的線程

Thread t = Thread.currentThread();

通過getMap傳入當前線程,獲取到一個ThreadLocalMap類型的Map。

ThreadLocalMap又是啥?

從命名來理解,就是一個存ThreadLocal的Map啦,Map就是使用key-value存儲形式的數據結構啦。

通過map.getEntry傳入這個ThreadLocal對象獲取到Entry(保存一對key-value的一個實體類),通過這個Entry的value拿到了Looper。

ThreadLocal.java

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

getMap 也是一行代碼,夠簡單的。

它返回了線程的一個變量??

沒錯,我們平時創建線程用的Thread類,它就是有這麼個變量。

3.引入ThreadLocalMap

Thread.java

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

註釋說(個人翻譯的,不知道準不準確,我就假設準備?):ThreadLocal的values值與此線程有關,這個Map由ThreadLocal來維護。

從翻譯知道:Thread告訴我們,這個Map是跟我有關的,但是我不管了!讓ThreadLocal自己維護!

what?搞什麼飛機,ThreadLocal怎麼維護???這可是你自己的東西。

好吧,我們看看ThreadLocal怎麼維護。先看看ThreadLocal裏的set方法

ThreadLocal.java

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

分析下這段代碼:

ThreadLocal對象拿到當前線程,然後,

還記得getMap方法嗎

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
}

通過getMap方法拿到這個線程的 ThreadLocalMap ,如果map不是null,就把自己(ThreadLocal對象)作爲key, 傳入的value作爲value,這對key-value放到map裏。如果map是null,就創建一個新的Map,然後把key-value放進去。

對我們Looper來說,value值就是Looper對象。

Thread真夠懶的,自己的變量自己不維護,讓ThreadLocal自己用set、get方法搞定了。

當然了,ThreadLocal處理的線程那也是有要求的:當前運行的線程。即,誰搞我(調用我的方法),我搞誰。

通過上面一系列麻煩的操作,Looper成功地讓和當前線程進行了綁定處理:讓當前線程的ThreadLocalMap中含有了鍵值對:

------------------------------------------------

key                                                       -    value      |

ThreadLocal(Looper的內部變量) -   Looper    |

------------------------------------------------

等等,好像哪裏不對,創建Handler的時候,Looper根本沒有讓ThreadLocal運行過set方法啊?

沒有綁定過線程啊,Looper.myLooper() 不就拿不到Looper嗎?

其實,主線程的Looper,在Android環境初始化的時候已經創建好咯。

Looper.java

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

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

請看prepareMainLooper方法的註釋:

應用程序的Looper是由Android環境創建的,因此您無需自己調用此函數。

至此就分析完遼。

劃重點總結:

Thread有一個自己不想管的Map叫ThreadLocalMap,它的這個ThreadLocalMap是讓ThreadLocal來管(Thread在放權啊?)。

ThreadLocal他就把自己作爲鍵,把想保存的東西作爲值,保存了到了調用他的線程的Map中。這樣,鍵和值就和當前線程建立了關係。

課後習題:

同一個Activity主線程中可以創建多個Handler嗎?

答案:當然可以,同一個Activity中的Handler們,拿到的都是主線程的Looper喔,用的同一個消息隊列。

附(舊的總結):

總結0.ThreadLocal和Thread的關係

每個Thread中都有一個存儲ThreadLocal的Map對象,這個Map不是使用的集合類中的Map,用的是一個叫做“ThreadLocalMap”(從名字可以明顯看出來它的作用了)的類來存儲。ThreadLocalMap是一個可以存儲key-value的map,key就是ThreadLocal,value就是要存儲的跟ThreadLocal對應的值。

這裏的ThreadLocalMap是ThreadLocal的“基友類”。

下圖看下兩者關係

總結1.線程綁定

問起Handler的原理,會提起Looper和MessageQueue跟線程綁定,balabala。

這裏實現線程綁定就是使用的Thread中的ThreadLocalMap來實現。

如何使用ThreadLocal呢。首先需要一個Manager類來持有一個ThreadLocal<T>的實例。不同的Thread將這個實例和想存儲的值保存起來就好咯。ThreadLocal提供了set、get等等方法來方便獲取或者保存當前運行線程(Thread.currentThread()可以獲取當前線程)對應的存儲值。這樣爲了不同的功能,我們可以定義不同的ThreadLocal,而且互不干擾。

線程綁定,直接寫成Map<Thread, value>不就可以實現嗎,爲什麼要寫得如此複雜。

這種存儲結構的好處:

1、線程死去的時候,線程共享變量ThreadLocalMap則銷燬。

2、ThreadLocalMap<ThreadLocal,Object>鍵值對數量爲ThreadLocal的數量,一般來說ThreadLocal數量很少,相比在ThreadLocal中用Map<Thread, Object>鍵值對存儲線程共享變量(Thread數量一般來說比ThreadLocal數量多),性能提高很多。

摘自:https://www.cnblogs.com/coshaho/p/5127135.html

2.Handler

 Hander有一個相關類爲Looper,這個就是上面所說的Manager類。

 Looper有幾個屬性值:

// 存儲Looper的ThreadLocal,可以看出Looper不僅充當Manager,還作爲基礎數據類(存儲MessageQueue等信息)
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

// 主線程的Looper
private static Looper sMainLooper;

// Looper對應的MessageQueue
final MessageQueue mQueue;

可以看出Looper不僅充當Manager,還作爲基礎數據類(存儲MessageQueue等信息),通過sThreadLocal,實現了線程和Looper的綁定。

Looper有個重要方法:

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

由此,大部分Looper的代碼基本上可以知道是什麼意思了。

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