1.简要
有些初级开发者总是认为Handler是用来更新UI的。这从其主要使用场景上讲,是没有问题的。但是要想更好的去了解Handler及其背后的运行机制,这个认识往往会导致对Handler理解的不够深刻,可谓是一叶障目,不见泰山。(PS:我在面试过程中,经常会考察面试者对于Handler的认识)
片面认识—— Handler是用来更新UI的。
2. 面试迷茫点
No Looper; Looper.prepare() wasn’t called on this thread.
Only one Looper may be created per thread
上面这两个异常,有些开发者可能在实际开发工作中都没有遇到过。究其原因,没有在子线程中去创建过Handler。
这也是有很大一部分面试者,谈到Handler的运行机制,用法及注意事项时,能做到侃侃而谈。下面列出几种常见场景:
- 当谈起多线程通信时,有一些面试者不知所云,但是说Handler,立刻就精神焕发。
- 涉及到HandlerThread的存在意义,有些茫然无措。
- 涉及到Handler,Thread,Looper,MessageQueue之间的对应关系时,有些说不清楚。
个人认为,这都是对于Handler的片面认识导致的。
3. Thread 和 Looper的婚姻关系
废话一句,我还是希望大家能仔细看一下上面的文字…
No Looper; Looper.prepare() wasn’t called on this thread. 不存在Looper——在该线程中没有调用Looper.prepare()方法。
Only one Looper may be created per thread 一个线程只能创建一个Looper。
从上面的错误提示中,我们可以得出一个结论:线程和Looper是1:1对应的关系
我们今天这篇文章主要探讨的是:如何保持这种正常的婚姻关系?
在Looper类中,你会看到:
ThreadLocal成员变量
// sThreadLocal.get() will return null unless you've called prepare().
// 若不调用prepare()方法则返回null
@UnsupportedAppUsage(只支持APP使用)
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
prepare()方法
// 清楚地看到该变量的set()/get()
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 注意这里:新建Looper,我们看看Looper构造函数干了些什么
sThreadLocal.set(new Looper(quitAllowed));
}
Looper方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
是谁在努力维持着这段美好的婚姻(Looper 和 Thread )呢?
Oop, ThreadLocal.
3.浅识ThreadLocal
1. set()方法
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. 给当前线程设置一个指定的值。
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
key-value形式:当前线程为key,value就是我们传过来的值。这唯一性就保持住了。So easy…
看见这个ThreadLocalMap,每个人都会猜测,它难道是一个HashMap 或者HashTable,抑或是 CocurrentHashMap。走过去看一下吧。
/*ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values
* /
static class ThreadLocalMap {
}
ThreadLocalMap是一个只适用于维护thread local 的自定义键值对。
4. 总结:
- Looper 和Thread 是 1V1的关系,其由ThreadLocal来维持关系;
- ThreadLocal 内部是由ThreadLocalMap来存储的,key 就是当前线程。
欢迎继续收看 :Handler的前世今生2——Looper