Handler的前世今生1——ThreadLocal

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的运行机制,用法及注意事项时,能做到侃侃而谈。下面列出几种常见场景:

  1. 当谈起多线程通信时,有一些面试者不知所云,但是说Handler,立刻就精神焕发。
  2. 涉及到HandlerThread的存在意义,有些茫然无措。
  3. 涉及到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. 总结:

  1. Looper 和Thread 是 1V1的关系,其由ThreadLocal来维持关系;
  2. ThreadLocal 内部是由ThreadLocalMap来存储的,key 就是当前线程。

欢迎继续收看 :Handler的前世今生2——Looper

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