本文以android-26的源碼為基礎(chǔ)進行分析
Looper與ThreadLocal
首先我們從Looper的源碼開始
在使用Handler和Looper的時候,我們知道,Handler發(fā)送消息給Looper,加入其消息隊列,Looper則不斷循環(huán)去除隊列前端的消息,并執(zhí)行Handler中的回調(diào)代碼。
但是Looper在使用前需要先執(zhí)行Looper.prepare()操作,否則會報如下的錯誤: Can't create handler inside thread that has not called Looper.prepare(),因為此時Looper對象并沒有初始化
那么我們來看一下Looper.prepare()做了什么
public final class Looper {
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));
}
}
從sThreadLocal.set(new Looper(quitAllowed)) 這一句代碼我們發(fā)現(xiàn), Looper.prepare()當中new了一個Looper對象,并將這個Looper對象設(shè)置進了一個叫做的東西里去,我們想要取當前線程的Looper對象的話,可以直接調(diào)用Looper.myLooper()這個靜態(tài)方法。
public final class Looper {
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
}
來看一下sThreadLocal的定義:
public final class Looper {
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
}
我們發(fā)現(xiàn)這個sTheadLocal是Looper中的一個靜態(tài)對象,類型為ThreadLocal,接下來我們需要解釋一下ThreadLocal是個什么東西
ThreadLocal
ThreadLocal的一個特點就是線程間互相隔離,在每個線程取同一個ThreadLocal對象都會得到一個不同的值。例如Looper, 每個線程的都只有一個Looper,且在各個線程中調(diào)用Looper.myLooper()的方法都會返回其自己的Looper。
在上面我們看到Looper.myLooper()方法通過調(diào)用sThread.get()方法獲取到了自己對應(yīng)的Looper,那么我們來看一下ThreadLocal的get()的方法。
public class ThreadLocal<T> {
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();
}
}
ThreadLocal中的get()方法首先獲取當前的thread對象,然后得到了當前Thread對象中的ThreadLocalMap對象,以當前的ThreadLocal對象為key獲取到ThreadLocalMap存的value,在這個例子中這個value就是Looper啦。
暫停一下,咱們捋一捋,首先Looper通過其中的靜態(tài)變量sThreadLocal.get()方法獲取到當前線程的ThreadLocalMap對象threadLocals 。然后又以sThreadLocal這個對象本身為key取到了threadLocals存著的Looper對象。
看著好像有點繞,那么我們換一種說法,每個Thread都存了一個map(實際是個數(shù)組),可以以Looper中的靜態(tài)對象sThreadLocal為key,在這個map中取到這個線程的Looper。由于sThreadLocal是個靜態(tài)對象,所以對于任意一個線程,這個key是固定的,這樣一來,通過Thread.currentThread()獲取到了某個線程就等于獲取到了其中的Looper對象。
這么一來是不是就清晰了很多,Looper中之所以要定義這么一個sThreadLocal的靜態(tài)對象,實際上就相當于定義了一個全局存在的static final的key,只不過這個key是ThreadLocal對象而已。
/** 本文不會對ThreadLocalMap進行更深入的分析,各位可以自行閱讀源碼去查看其具體實現(xiàn)**/
public class Thread implements Runnable {
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
}
最后我們稍微圓一圓整個流程,現(xiàn)在我們知道了我們獲取到了一個Thread,就能以sThreadLocal為key get到其中的Looper對象。那么我們是什么這個Looper對象set到對應(yīng)的Thread當中的呢。記不記得文章開頭Looper.prepare()的過程,其中調(diào)用了sTheadLocal.set(T)方法,最后是怎么將Looper對象綁定到線程上的,來看一下源碼:
public class ThreadLocal<T> {
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
}
首先獲取到當前線程t,然后得到其中的ThreadLocalMap對象,然后將這個Looper對象放到了map中對應(yīng)sThreadLocal的地方。相信這里大家都能很輕松的理解了。
最后總結(jié)一下ThreadLocal存在的意義:
- 封裝了對當前所在線程中ThreadLocalMap的set和get操作,默認以自身作為key值進行數(shù)據(jù)存儲
- 以泛型對外暴露,支持了所有數(shù)據(jù)類型
因此在使用ThreadLocal的時候,每一個數(shù)據(jù)都需要新建一個ThreadLocal對象為key
例如針對每一個線程都要存儲一個String,需要新建一個ThreadLocal<String>對象,如果希望在任何地方都能夠獲取到這個String,則該ThreadLocal<String>對象需要是用static進行修飾。
