提及Android的消息机制,相信这是Android开发者非常熟悉,并且相当基础的一个知识点了。那这里还有什么需要讲的呢?这里,先抛出一个问题:
问题
我们在使用Handler的时候,都知道其必须要跟一个Looper绑定的。而在UI线程可直接初始化Handler来使用,但是在子线程则不行,系统会抛出一个必须调用Looper.prepare()
的异常信息。缘由在于,当初始化Handler
的时候,其会通过Looper
来获取当前的Looper,代码如下:
1 2 3 4 5 6 7 8 9 10 |
|
那么,问题来了,为什么在子线程中,通过Looper.myLooper()
方法获取的就是为空呢?如果有人回答了Looper是线程相绑定的,那它是如何做到绑定的? 如果还知道答案的话,那就可以跳过本篇文章了。
代码分析
1. Looper的myLooper方法
1 2 3 |
|
此方法只是通过从变量sThreadLocal
中取出一个值。那么它的值是哪里来的呢?
2. Looper的prepare方法
1 2 3 4 5 6 |
|
可以看出的是调用了这个方法之后,会在sThreadLocal
中存在一个新建的Looper
对象。那么看看这个sThreadLocal
是什么东西呢?
3. sThreadLocal的定义
1 2 |
|
是一个静态的ThreadLocal的变量,并且泛型指向的是Looper
对象。另外,注释中告诉了我们,如果我们不调用prepare
方法的话,那get
方法返回的是null。所以现在就引出咱们的重头戏ThreadLocal
了。
ThreadLocal
1. 定义
先看一下官方的解释:
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
对象,
并且当一个线程的值发生改变之后,不会影响其他的线程的值。
2. 实现
ThreadLocal的类定义使用了泛型ThreadLocal<T>
,其中T指代的是在线程中存取值的类型。(对应Android中使用的ThreadLocal, T则存放的类型为Looper)
+ set方法
1 2 3 4 5 6 7 8 |
|
其中的values
方法如下:
1 2 3 |
|
方法中,先通过Thread.currentThread
来拿到当前线程,再拿到线程的values属性,并对此values属性进行赋值,其中key为当前的ThreadLocal
对象,value则是当前要存放的值。而这个values对象,其中维持了一个一维的object数组,采用偶数为key, (索引为index)奇数为value(索引为index + 1)的数据结构。
+ get方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
在进行取值的时候,也是现获取当前线程,然后根据当前ThreadLocal的hash值与values的mask标志位进行与操作,来获取到当前ThreadLocal在这个线程的values中的位置,并通过判断其存放的key是不是当前ThreadLocal,若是的话,则返回index+1对应的值,即是我们所存放的值;若不是的话,则需要通过values的getAfterMiss方法来进行更进一步详细的搜索。
总结
ThreadLocal通过获取当前线程中的values属性,从而实现了每个单独线程的信息绑定。这样的话,Android的消息机制中,Looper便是采用ThreadLocal作为存储结构,所以looper对象的存储只会在当前线程中,子线程若是使用消息机制的话,必须调用Looper.prepare方法来在线程中新建一个Looper的对象。