Android的Handler处理消息机制(Looper、MessageQueue)

撸了一下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进行更新的操作,不然就会抛异常!

 

画了个简单的流程图,仅供参考!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值