參考資料:
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數量多),性能提高很多。
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的代碼基本上可以知道是什麼意思了。