相关链接:
http://www.jianshu.com/p/a8fa72e708d3
创建Handler的时候,需要先创建当前线程的Looper,Android默认在启动的时候为我们创建的 主线程的Looper,所以我们可以在主线程直接创建Handler。
但是在子线程创建Handler的时候,我们必须先手动创建Looper,才能创建Handler,否则会抛出一个必须调用Looper.prepare()
的异常信息。
原因:
public Handler(Callback callback, boolean async) { //省略 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } //省略 }
问题:
相同的代码,为什么在主线程创建就可以,在子线程创建就异常???为什么在子线程中,通过Looper.myLooper()
方法获取的就是为空呢?
查看Looper.myLooper()源码,可知道原因:ThreadLocal获取的是“当前线程”的Looper
sThreadLocal.get();
ThreadLocal在什么时候set值的那?--- 在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)); }
ThreadLocal的初始化:--- 在Looper中源码中
// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
看注释可以知道,sThreadLocal.get()之前,必须Looper.prepare(),否则get到的值为null。
ThreadLocal是什么?
看一下官方的解释:
Implements a thread-local storage, that is, a variable for which each thread has its own value. All threads share the same {@code ThreadLocal} object, but each sees a different value when accessing it, and changes made by one thread do not affect the other threads. The implementation supports {@code null} values.
意思是说:ThreadLocal实现与线程相关的存储。所有线程共享一个ThreadLocal对象,但是不同的线程有自己的值。并且当一个线程的值发生改变之后,不会影响其他的线程的值。
所以这就解释了为什么 在子线程中,如果不Looper.prepare()就直接创建Handler的时候,会抛出异常。
ThreadLocal并不是线程,它的作用是可以在每个线程中存储数据,,数据存储以后,只有在指定线程中可以获取到存储的数据。对于其它线程来说无法获取到数据。
ThreadLocal是一个关于创建线程局部变量的类。通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改。
下面看一下ThreadLocal部分源码:
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value);//创建当前线程的map(仅第一次调用) } ThreadLocalMap getMap(Thread t) { return t.threadLocals; } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue);//this代表的是ThreadLocal } public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue();//null } private T setInitialValue() { T value = initialValue();//null Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; } protected T initialValue() { return null; }
所有线程共用一个ThreadLocal,每一个线程都有一个ThreadLocalMap,key为当前线程的ThreadLocal。
所以今后,如果多线程的每一个线程都有一个相同变量,但是每个线程的变量值只有当前线程自己可以修改,不受其它线程的影响,那么你就可以用ThreadLocal。多线程共享同一个ThreadLocal对象。