Handler、Looper、ThreadLocal 、Values

1.Looper創建過程

Looper主要方法如下:
- prepareMainLooper()
- prepare()
- loop()
- Looper(boolean quitAllowed)

  Looper的創建過程我們就從應用程序的主線程(UI線程)的Looper的創建過程說起。UI進程的創建是在ActivityThread的main()的主方法開始的,代碼如下:

    public static void main(String[] args) {

       //looper不相關代碼已移除 ①
        Looper.prepareMainLooper();

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

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        //啓動looper循環 ②
        Looper.loop();

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

  main()方法中①處調用了Looper.prepareMainLooper()方法。prepareMainLooper()的內部實現較爲簡單,實際調用了Looper.prepare()方法,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、Thread關係緊密的對象成員ThreadLocal。sThreadLocal.get()將獲取一個ThreadLocal對象(文章下面記錄get()方法的內部實現)。並判斷獲取的對象是否爲null,如非null將拋出異常”Only one Looper may be created per thread”此處也是保證一個線程只有Looper對應的關鍵點。代碼④中new Looper()實例並存儲到Values中。

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

  在Looper構造方法中創建了存放Message實例的消息隊列並將當前所在線程賦值給自己的mThread變量。

  在代碼②處調用了Looper.loop()方法。loop()具體實現如下:

    public static void loop() {
        //⑥myLooper()內部調用了ThrealLocal的get方法()
        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;
        //實現爲一個死循環不斷從Looper的MessageQueue消息隊列中去除消息並處理
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            //無關代碼以去除

            //⑦msg.target爲發送消息的Handler實例
            //在此調用了handler的dispatchMessage()
            msg.target.dispatchMessage(msg);

            // 無關代碼以去除
        }

  在調用Looper.loop()方法之前必須先調用Looper.prepareMainLooper()方法。然後從獲取的looper實例中獲取消息隊列對象並啓動一個死循環不斷的從消息隊列中取得消息。在代碼⑦處是Handler消息處理的關鍵,Message對象中持有一個發送這個消息的handler實例的一個引用。msg.target是一個Handler實例,在此調用msg.target.dispatchMessage(),在dispatchMessage()方法內部實際上是調用了Handler的handleMessage()方法。文章下面在介紹Handler時會介紹他們的具體實現。

2.ThreadLocal

主要方法如下:

  • get()
  • set()

Implements a thread-local storage, that is, a variable for which each thread has its own value. All threads share the same {@code ThreadLocal} object, but each sees a different value when accessing it, and changes made by one thread do not affect the other threads. The implementation supports {@code null} values.

  大致意思是ThreadLocal實現了線程本地存儲。所有的線程都共享同一個ThreadLocal對象,每個線程都有一個自己的值。每個線程在訪問這個值時不影響其他線程相對應的值。且這個值允許爲空。

  ThreadLocal爲編寫多線程併發程序提供了一個新的思路。如下圖所示,我們可以將ThreadLocal理解爲一塊存儲區,將這一大塊存儲區分割爲多塊小的存儲區,每一個線程擁有一塊屬於自己的存儲區,那麼對自己的存儲區操作就不會影響其他線程。對於ThreadLocal,則每一小塊存儲區中就保存了與特定線程關聯的Looper。

image

Thread、ThreadLocal和Values的關係

  Thread的成員變量中有類型爲ThreadLocal.Values的localValues變量,由於線程特定變量可能會有多個,並且類型不確定,所以ThreadLocal.Values有一個table成員變量,類型爲Object數組。這個localValues可以理解爲二維存儲區中與特定線程相關的一列。
ThreadLocal類則相當於一個代理,真正操作線程特定存儲區table的是其內部類Values。

image
image
  ThreadLocal和Values的關係和原理在上面已經做了簡要說明,那麼繼續從代碼④處說起。在ActivityThread的main()方法中第一次創建肯定是會執行代碼④的邏輯。在此暫時忽略new Looper(quitAllowed)的內部實現來看一下sThreadLocal.set()到底做了什麼:

    public void set(T value) {
        //獲取當前線程
        Thread currentThread = Thread.currentThread();
        /**
        *獲取當前線程中持有的Values對象
        *values(Thread current)直接返回了
        *current.localValues對象
        */
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        //⑤
        values.put(this, value);
    }

    public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }

        return (T) values.getAfterMiss(this);
    }

Values內部實現

  在第一次綁定looper對象前value值肯定爲null,上述set()即會執行initializeValues()。內部實現直接new了一個Values對象且負值給了Thread.localValues對象。在Values()的構造方法中初始化了內部Object[]的大小(默認大小是16 * 2,Object[] table中交叉存儲Key和value)和其他一些初始化。

  代碼⑤Values.put(ThreadLocal

    void put(ThreadLocal<?> key, Object value) {
            cleanUp();

            // Keep track of first tombstone. That's where we want to go back
            // and add an entry if necessary.
            int firstTombstone = -1;

            for (int index = key.hash & mask;; index = next(index)) {
                Object k = table[index];

                if (k == key.reference) {
                    // Replace existing entry.
                    table[index + 1] = value;
                    return;
                }

                if (k == null) {
                    if (firstTombstone == -1) {
                        // Fill in null slot.
                        table[index] = key.reference;
                        table[index + 1] = value;
                        size++;
                        return;
                    }

                    // Go back and replace first tombstone.
                    table[firstTombstone] = key.reference;
                    table[firstTombstone + 1] = value;
                    tombstones--;
                    size++;
                    return;
                }

                // Remember first tombstone.
                if (firstTombstone == -1 && k == TOMBSTONE) {
                    firstTombstone = index;
                }
            }
        }

  從put()方法的實現邏輯可以明白Values中Object[] table是如何在工作:他的存儲結構如下:
這裏寫圖片描述

  在table數組中是交叉存儲ThreadLocal和value值的。假設i處存儲ThreadLocal實例那麼i + 1處就會存儲和ThreadLocal實例結對的value值。上圖中Object實例的位置就是存放looper實例的位置。

Handler

主要方法如下:
- handleMessage(Message msg)
- dispatchMessage(Message msg)
- sendMessageAtTime(Message msg, long uptimeMillis)

在Android系統中只有Main thread才能更有應用程序UI界面。所以要將耗時任務放到子線程中執行,否則會產生ANR錯誤(UI進程中執行任務超過5秒回報錯)。任務在子線程中執行完畢需要更新UI界面,一般情況下我們會採用Handler來接收子線程的Message消息在主線程中更新UI界面。

  Handler一般我們主要用於實現在子線程中處理耗時任務後更新UI界面的功能。通過重寫handleMessage(),並在其中實現操作UI更新的邏輯。Handler類有多個構造方法,每個方法內部實現都不復雜就不糾結了。

  • public Handler()
  • public Handler(Callback callback)
  • public Handler(Looper looper)
  • public Handler(Looper looper, Callback callback)

第一個和第二個構造函數中沒有傳入Looper,這兩個構造函數都將通過調用Looper.myLooper()獲取當前線程綁定的Looper對象。
“`java
public Handler() {
this(null, false);
}

public Handler(Callback callback) {
    this(callback, false);
}

public Handler(Callback callback, boolean async) {

    //刪除無關代碼

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

“`
通過這個Looper實例獲取了其中保存的MessageQueue(消息隊列)。每個Handler對應一個Looper對象,產生一個MessageQueue.

#### Callback
  第二個和第四個構造函數還傳遞了Callback對象,Callback是Handler中的內部接口,需要實現其內部的handleMessage方法,Callback代碼如下:
java
public interface Callback {
public boolean handleMessage(Message msg);
}

  使用Handler有兩種方式:第一種繼承Handler類並實現其handleMessage();第二種在構建Handler對象時參數中傳入Callbck的實現類對象。在出入Callback對象的情況下會優先調用Callback的實現。具體邏輯會在dispatchMessage()方法中看到。源碼如下:
java
public void dispatchMessage(Message msg) {
//⑧Message的Runnable
//在此暫時不做介紹
if (msg.callback != null) {
handleCallback(msg);
} else {
//⑨使用Callback的實現類來處理消息
//這種情況下不會再走handler的handleMessage()
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//⑩在沒有Message.Runnableh和Callback實現的情況下
handleMessage(msg);
}
}

從dispatchMessage()的實現中可知在Message攜帶Runnable的實現時就是調用Runnable的run方法。在沒有runnable實現的情況下如果存在Callback的實現會調用Callback.handleMessage()方法。在兩者都沒有時會最終調用handler的handleMessage()。

由此可見三者的優先級關係爲Message.Runnable > Callback.handlerMessage() > Handler.handleMessage();
#### 發送消息
Handler發送消息的方法衆多,其中大部分方法最終會調用Handler的sendMessageAtTime()方法。sendMessageAtTime()的具體實現如下:
java
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);
}

從上邊代碼可以看出sendMessageAtTime()最後也是調用了enqueueMessage()讓消息進入隊列:
java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//⑪消息將入消息隊列
return queue.enqueueMessage(msg, uptimeMillis);
}

其實消息的發送本質上就是將Message按照時間進行排序後放入到MessageQueue中去

這部分代碼看過幾次不是很理解,就記錄了下來。其中有部分是之前做筆記摘錄的,不知出處如有不妥請告知。以上個人理解不當之處請務必告知,謝謝

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