有关Handler原理的文章已经烂大街了,有很多文章写的特别棒。这篇文章其实是我想自己简短总结一下自己的理解,和我个人当初在学习时,比较疑惑的地方。
Handler是干什么的?
个人理解:它是一个用来发送消息,并且自己处理这个消息的一个通信工具。(自己发自己处理?对!)关键是它可以在不同线程发送或处理消息
使用场景:
主要用于线程间的通信。
原理:
handler发出一个消息,放入到一个容器,外部启动一个死循环,不停的判断容器中是否有消息,有就拿出来,还给handler处理。
怎么就实现在线程间通信了?
只说关键流程:
1、在线程A中要预先创建一个Looper, 同时在Looper中会创建一个MessageQueue对象。
2、在线程A中创建Handler对象并重写了handleMessage方法,等待消息到来处理消息。也就是处理消息的线程是线程A。在Handler构造方法中,会去拿到上面这个Looper对象,和Looper中的MessageQueue对象
3、在线程B发送消息,Handler会把消息放入到MessageQueue中,但在这之前,把自己赋值给了这个Message的target变量。 也就是说,在消息中会有一个target变量就是当初发送自己的handler对象
4、调用Looper中的loop()方法。方法开启了一个死循环,判断当前Looper中的MessageQueue中是否有消息Message,如果有就调用该消息的target变量(handler对象)的dispatchMessage()方法。
5、处理消息,在Handler中的dispatchMessage()方法中分发消息,判断具体把消息交给handler的那个回调方法处理。
最后结果就是,线程B中发出的消息,在线程A中得到处理。。。注意第4步流程,可以直接在第1步之后做,总之是要开启这个死循环。
其他文章还介绍了ThreadLocal, 这里也说下,ThreadLocal可以创建一个只属于线程自己的变量,这个变量与其他线程是隔离的,不会被共享。线程A中创建的Looper对象就是放到了的ThreadLocal中。Handler构造中也是通过ThreadLocal去拿到的Looper对象。
就不贴代码了吧。其他文章都有,这里就是自己做个小总结。
当初疑惑的地方
为什么这么设计?
我不知道还有其他的办法,来实现类似的功能。在看一些文章和听一些人讲解后,应该只有这种方式可以满足此类场景的的需求。就是在一个线程中及时处理其他线程发过来的消息。必须要有一个循环来不停的检查是否有消息传递。还有就是考虑到要想一个Android程序保持运行状态,就必须有代码一直在执行,不能写个阻塞在程序里吧,还要不停的刷新UI。这个死循环的设计正好可以满足。(从ActivityThread中的main()来思考)
Looper.loop()死循环,不阻塞线程吗?不消耗cpu资源吗?
首先上面也提到了Looper.loop()在ActivityThread中调用是等于是保持了程序的运行状态,每一次循环等于一次完整的刷新逻辑。只要loop()中每次循环过程是流畅的,展现给我们看到的就是流畅的操作。只有你在loop()中的某次循环中写了一个死循环,导致loop()循环卡主了,我们才会看到应用卡了。
有消息正常处理,正常消耗cpu资源,当没有消息时候,线程会释放CPU资源进入休眠状态。具体涉及到Linux epoll的阻塞 唤醒机制。到这里,我就停止了。
同一个线程中创建多个Handler,消息不会乱吗?
target! 每个消息都有保存了自己的Handler对象,分发消息时候当然知道是哪个handler去处理(步骤3)。那handler放在MessageQueue中可以吗?这样就不用每个消息都存个handler了。不可以!同一个线程中,多个Handler拿到的MessageQueue是同一个。一个线程可以创建多个Handler,但只能有一个Looper,一个Looper中只能有一个MessageQueue(查看Looper.prepare()源码)。也就是说,同一个线程中Handler共享同一个Looper和MessageQueue。
处理消息有优先级吗?
可以说有,也可以说没有,看你怎么理解优先级这个概念了。Message中是没有设置优先级这种字段的。但有一个setAsynchronous(boolean async)方法,可以把消息标识成异步消息。Message消息本质上没有优先级,但它有3种类型,1、同步消息(平常我们发的都是),2、异步消息,3、屏障消息(没有target)。发送了一个屏障消息之后,会先处理异步消息,阻塞住同步消息,直到屏障消息移除。这么来看也可说通过同步屏障来控制处理消息的优先级了。
文章所描述的观点问性问题,是出于自己的认知,不保证正确。欢迎指正文章错误。