Handler源码解读

栏目: IOS · Android · 发布时间: 5年前

内容简介:大家面试的时候是否经常被问到有没有看过handler源码,如果回答看过了接下来就各种深入提问,直到问得你云里雾里面试官才开心。今天作者带领大家一起深入地过一遍handler源码,将面试常用问题各个击破,从此手捧offer,和面试失败say no!看源码首先当然是从使用处看起,我们首先来看一下平时项目中handler的使用,我们首先定义了一个handler对象,并且写好了接收到消息以后需要执行的操作,然后在子线程中发送一个消息。

大家面试的时候是否经常被问到有没有看过handler源码,如果回答看过了接下来就各种深入提问,直到问得你云里雾里面试官才开心。今天作者带领大家一起深入地过一遍handler源码,将面试常用问题各个击破,从此手捧offer,和面试失败say no!

看源码首先当然是从使用处看起,我们首先来看一下平时项目中handler的使用,我们首先定义了一个handler对象,并且写好了接收到消息以后需要执行的操作,然后在子线程中发送一个消息。

问题一  handler发送的消息是怎么到达handlerMessage回调的呢?

Handler源码解读

Handler源码解读

首先我们就从源头开始看起,handler.senMessage()方法一路点下去,发现最终调用的是MessageQueue类的enqueueMessage方法,也就是说最终是把message添加到了消息队列MessageQueue中,如下图所示

Handler源码解读

OK,消息放进去了,但是我们发现这里面并没有调用handler的handleMessage方法,所以我们换个角度想,放进去了总要取出来吧,不然放进去就没有意义了。于是我们就在MessageQueue中寻找返回值为Message的方法,果不其然找到了一个next方法,在该方法中返回了刚刚保存好的message,如下图

Handler源码解读

那么问题又来了,究竟是谁调用了这个next方法把消息取出来了呢?解决这个问题就要反过来考虑,需要调用next方法就需要实例化MessageQueue这个类,不然没法调用,MessageQueue是在Looper中初始化的,所以接下来我们来看看Looper类的源码。在Looper源码中是在构造函数中初始化MessageQueue的

Handler源码解读

接下来我们再搜一下调用next方法的地方

Handler源码解读

我们发现在Looper的loop方法中有一个无限循环,其中每次循环都调用MessageQueue的next方法,如果找到了就会执行msg.target.dispatchMessage(msg),那么msg.target是什么东西呢?我们接下来就点开Message类,查看一下target属性,如下

Handler源码解读

哇塞,原来是Handler对象,这里其实就是调用的handler的handlerMessage方法,现在逻辑就清晰了。Handler首先将我们发送的message保存到MessageQueue,然后Looper获取到message以后调用Handler的handlerMessage方法将message返回。

问题二 Handler是如何实现跨线程通信的?

纵览全局,我们发现,其实message相当于一个物品,MessageQueue相当于一个仓库,被handler放到MesageQueue,然后被Looper取出来还给handler,对于物品来说是不会挑选主人的,类的属性也一样,无论哪个线程都可以操作修改。所以message在子线程被放进去以后在主线程被取出来就很正常了。

Handler源码解读

问题三 Looper是在哪里被调用的?

我们平时使用Handler一般是子线程和主线程通信,而主线程的Looper其实在app初始化的时候就已经初始化并且开始Loop无限循环获取Message了,具体代码在ActivityThread的main方法中,其中prepareMainLooper就是初始化Looper,然后还调用了loop方法。如下图

Handler源码解读

问题四 如何保证Looper唯一性,一个线程不会出现多个Looper?

我们都知道,如果同一线程每次都new一个Looper的话就没办法保持唯一性。想要知道这个知识点,必须看一下Looper是怎么设置和获取的,我们点进去初始化的源码,如下图

Handler源码解读

Handler源码解读

我们发现,Looper的设置和获取并不是普通的属性get和set,而是通过ThreadLocal类来操作,所以我们点进去ThreadLocal看一下,发现里面Looper的设置和获取操作都需要传入线程参数,也就是说以线程为Key,对应了唯一的Looper,其实Looper唯一也就意味着MessageQueue也是唯一的,因为MessageQueue是在Looper构造方法里初始化一次的,但是Handler可以有多个,因为我们每初始化一个Handler对象,都会被Message记录,最后调用相应Handler的handleMessage方法,并不会有冲突。其中ThreadLocal代码如下

Handler源码解读

问题五 MessageQueue是一个什么样的数据结构?

分析这个问题,我们首先要找到其设置数据的地方,如下图所示,MessageQueue保存了每条message的值和next指针,也就是说想获取到下一条消息必须要看上一条消息的next指向,这样就导致了数据的先进先出,显然是一个队列的实现。

Handler源码解读

问题六 消息是怎么实现延时的?

1.既然消息是保存在队列中,那么刚保存的时候就应该保存好该消息所在的位置才对,不然在获取的时候就没办法保证延时短的消息先发送了,如问题五中的图,这个的实现其实就是死循环对每个message的延时时间进行对比,如果当前消息的延时短则更改指针指向,将当前消息插入到队列中。

2.如果发送消息的时间没到,则不交给Looper。如下图,在Looper获取消息时会对时间进行判断,如果时间还没到则不返回message,即相当于本次循环并没有到达时间需要发送的消息

Handler源码解读

问题七 Handler机制中生产者和消费者模式体现在哪里?

前面说到handler中把消息放到了messageQueue,然后Looper从messageQueue中获取消息并使用,这个相当于就是一个生产者和消费者模式,一个负责生产一个负责消费。生产消费者模式中的阻塞在handler中也是体现得淋漓尽致,在获取消息时,如果消息池中消息数量为空,那么此时进入阻塞状态,如下图

Handler源码解读

最后 :本人小萌新,之前都是看得多写得少,现在也想把自己的所见所得记录下来给大家分享分享,若有写错或者没有补充完整的地方欢迎各位大神指教,以后有新的心得体会会及时更新本文,谢谢大家阅读!


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Spring Boot实战

Spring Boot实战

[美]克雷格·沃斯 / 丁雪丰 / 人民邮电出版社 / 2016-9 / 59.00元

本书以Spring应用程序开发为中心,全面讲解如何运用Spring Boot提高效率,使应用程序的开发和管理更加轻松有趣。作者行文亲切流畅,以大量示例讲解了Spring Boot在各类情境中的应用,内容涵盖起步依赖、Spring Boot CLI、Groovy、Grails、Actuator。对于Spring Boot开发应用中较为繁琐的内容,附录奉上整理完毕的表格,一目了然,方便读者查阅。一起来看看 《Spring Boot实战》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具