先说Handler、Looper、MessageQueue,这三者之间的关系还是比较简单的,Looper是消息的执行者,可以认为是代码的执行者,因此这个类和线程结合最紧密;MessageQueue就没什么说的了,就是存放消息的队列,主要用于管理消息,Handler这个是我们平时接触最多的,因为它决定如何处理消息,比如来了一个“闹钟”消息,那么我们就起床,来了一个“下班”消息,我们就下班,这个和具体业务有关,因此Handler我们使用的最多也就不足为怪了。
好了,我们把三者连成一条线,首先定义一个Handler,在handler内部需要初始化一个Looper,因为消息最终要有一个实际的执行,这个Looper可以由我们传入,默认是使用当前线程的Looper,如果传入一个其他线程的Looper,那么由这个Handler发送的消息最终都是由那个线程去执行的。然后我们的消息是放到消息队列中,Looper本身就是会去初始化自己的消息队列的,handler其实也是通过拿到Looper的消息队列之后,再往消息队列中塞消息的。
好了上面已经了解了大概流程,现在需要抓一些细节,比如:
上面handler初始化,默认获取当前线程的Looper,那么这个Looper是如何得到的?
Thread和Looper有什么关系?
其实如果要我们自己设计上面Thread和Looper的关系时,肯定会将Looper直接作为Thread的成员变量,然后通过get方法使得外部能够获取到这个变量,其实这种思路也可以,不过安卓使用了更加通用的方式ThreadLocal对象,使得同一个对象在任何线程都能得到不同的值,这就是线程私有对象,先来看看ThreadLocal的使用:
final ThreadLocal<Integer> t1 = new ThreadLocal<Integer>();
t1.set(100);
Log.v(TAG, "main:" + t1.get());
new Thread() {
@Override
public void run() {
Log.v(TAG, "t1:" + t1.get());
t1.set(200);
Log.v(TAG, "t1:" + t1.get());
super.run();
}
}.start();
new Thread() {
@Override
public void run() {
Log.v(TAG, "t2:" + t1.get());
t1.set(300);
Log.v(TAG, "t2:" + t1.get());
super.run();
}
}.start();
上面的打印结果是:
04-04 10:58:12.259: V/MainActivity(1523): main:100
04-04 10:58:12.269: V/MainActivity(1523): t1:null
04-04 10:58:12.269: V/MainActivity(1523): t1:200
04-04 10:58:12.269: V/MainActivity(1523): t2:null
04-04 10:58:12.269: V/MainActivity(1523): t2:300
也就是说t1这个对象在不同的线程有了不同的值,ThreadLocal内部也是使用键值对的形式来存储对象的,键就是上面的t1,值就是具体设置的值,设置的时候,会把这个键值对存储到Thread对象中,因为每个线程都有对应的Thread对象,所以归根到底私有对象还是基于Thread这个对象的事实。
那么Looper的键是哪一个呢?考虑到键需要被所有线程引用,所以肯定是堆中的对象,堆中的对象是线程共享的,另外避免这个键的引用在各个线程里面传递,因此作为静态变量是再合适不过了,因为类静态对象,仅需要类来引用,类本身是线程共享的。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();