在解释Handler原理时有个一个现象;每一个线程都要有自己专属的Looper对象,如果没有的话在创建Handler对象时会报错。参见:可以在子线程实例化Handler对象吗
那么线程Thread是怎么和Loope对应上的?尤其是多个线程和多个Looper的情况下是怎么实现的?这就是涉及到一个叫ThreadLocal的类,ThreadLocal类其实就是一个map集合类型的封装类,可以为不同的线程保留这个线程的本地变量。它在Looper中的用法也很简单,ThreadLocal被定义成静态变量,并且有两个与之对应的静态方法prepare和myLooper用于操作, 这里看一下源码:
// sThreadLocal.get() will return null unless you've called prepare().
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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));
}
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
prepare方法会先从sThreadLocal里获取Looper对象,如果为空则创建Looper并保存时sThreadLocal。而myLooper则直接获取Looper对象,如果获取不得话就返回空,ThreadLocal在Looper里的作用就这些。但问题是ThreadLocal是怎么知道哪一个Looper属于哪个Thread的?我们继续查看ThreadLocal的set方法:
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的本质是一个map集合的封闭类,set方法需要传一个变量作为map的value值,value是一个泛型,在Looper中自然是传入一个被实例化的Looper对象,然后再以当前的线程为key值进行保存或取出。所以说到这里应该就能明白多个Looper对象是怎么和多个Thread对象产生联系的吧!
最后说一下ThreadLocalMap这个类,通过名称我们可以认为它是一个map集合型的类,用法也和其它的map集合相似。但仔细看源码后我们发现ThreadLocalMap类并不简单;首先它是ThreadLocal的静态内部类,内部维护着一个类型为Entry的数组,对于ThreadLocalMap的操作就是围绕着这个数组展开的。而这个Entry居然也是一个静态内部类而且是弱引用以便于GC回收,Entry可以容纳一个Key和一个value值。定义如下:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
ThreaLocalMap的构造方法和相关的属性如下:(有省略)
private Entry[] table;
private int size = 0;
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
从构造器可以看出ThreadLocalMap对外提供的是map集合式的操作,但内部却是通过数组遍历的方法来实现的。
点击链接加入群聊【口袋里的安卓】:861401732
或关注微信公众号:
网友评论