撸了一下Android的源码,把Handler、Looper、MessageQueue以及Message的关系稍微捋了一下,发现其实只要明白了ThreadLocal的原理,就能明白为什么通过Handler发送Message就能在UI线程(主线程)更新UI了。
先说一下它们的执行流程:
Handler发送Message给MessageQueue,每个Looper里都有一个MessageQueue;调用Looper.loop()之后,Looper会不断的去MessageQueue里拿Message出来,然后交给Handler进行如下处理:
1、Handler先判断Message是否有设置了callback属性(Runnable接口的实例),如果有就直接调用Runnable接口的run(),然后这条Message就被消费完了;如果没有就进行下面第2步
2、Handler再判断是否有Handler.Callback接口的实例(实例化Handler时可以传进来),如果有就执行Callback.handleMessage()方法,然后这条Message就被消费完了;如果没有就进行下面第3步
3、直接执行Handler.handleMessage()方法,也就是我们平常实例化时经常重写的处理消息的方法。
那为什么我们在UI线程实例化出来的Handler就能更新UI呢?
先说一下ThreadLocal<T>类的机制,这里介绍一下它的两个主要方法:set()和get()。每个线程通过set()方法设置进去的实例对象,在当前线程通过get()方法能够获取到这个对象,但是其它线程是获取不到相同的这个对象。比如:
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
Student.local.set("小明");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + ": " + Student.local.get());
}
}.start();
new Thread(){
@Override
public void run() {
Student.local.set("小芬");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + ": " + Student.local.get());
}
}.start();
}
static class Student {
public static ThreadLocal<String> local = new ThreadLocal<>();
}
打印结果:
接着说,Handler有好几个构造方法,在这里我简单把它们分为两类:没有传Looper参数和有传Looper参数的。
先说第一种,也是我们经常实例化Handler使用的。在这个构造方法里,它会执行Looper.myLooper()来获取一个当前线程的Looper实例,并通过该Looper实例来获取MessageQueue(其实每次sendMessage都是把Message直接入队到该MessageQueue里)。
接着说一下为什么使用Looper都需要依次调用:Looper.prepare()和Looper.loop()方法:
Looper.prepare():会先通过ThreadLocal<Looper>实例获取Looper对象并判断是否为null,如果为null就直接抛异常;否则就实例化一个Looper对象并set到ThreadLocal<Looper>实例里。
Looper.loop():不断的从MessageQueue里拿出Message进行消费
根据上面的例子可知道不同线程set到ThreadLocal里的对象是不一样,那么通过UI线程set进去的Looper对象就只会属于UI线程,上面也提到了,Handler会把Message发送到MessageQueue里等待被消费,而正是Looper不断的从MessageQueue里拿Message出来进行消费,这样只要Looper是属于UI线程,并且由它来调用Handler.handlerMessage()方法,那么自然在handlerMessage()方法对UI进行更新操作就不会触犯了:UI更新只能在UI线程进行的天规了!
那么UI线程是什么时候初始化Looper的呢?
Looper它有个static 的Looper实例sMainLooper,它由Android Enviroment创建的(源码注释有讲)它是在UI线程通过调用Looper.prepareMainLooper()方法创建出来的,至于是什么时候创建的就没有深究了,有兴趣可以自行阅读源代码。
至于第二种Handler的构造方法,无非就是直接使用传进来的Looper来进行Message的处理,但是这里额外要注意的是:如果这个Looper不是属性UI线程的,那么在Handler.handleMessage方法里就不能对UI进行更新的操作,不然就会抛异常!
画了个简单的流程图,仅供参考!