举一反三系列

举一反三之Android Handler机制

2019-12-11  本文已影响0人  wp529

当我还是新手的时候,知道该看源码,但总是不知道怎么看源码,经常深入源码就迷路了,看过一遍后隔一段时间又忘记了。直到在某一篇博客上看到,看源码要带着问题去看,才能有的放矢,所以我的文章风格,抛出问题,查看源码寻求答案,再由答案引申其他问题。那么开始我的第一篇文章吧。

老生常谈Handler机制,不管新手老手都绕不过去的知识点,那么问题来了。

0.Looper取msg出来处理时都是取的第一个msg,那么有延迟的msg是怎么处理的?

1.message是怎么被处理?

2.如何做到的跨线程通信?

可别小看就这三个问题,解答完这三个问题里基本上就能全面了解整个handler机制了。

废话不多说,请各位打开源码与我一探究竟。

探寻答案之前我们必须知道贯穿handler机制的有四大对象,Handler,Message,MessageQueue,Looper,这些我就不一一介绍了,后续有介绍。但目前必须知道的是MessageQueue是一个链表数据结构,里面全是存的message。

问题0延迟msg怎么处理?

image

见上图,在每个被加入的msg都会为msg添加when属性,不指定则when默认为0。msg在加入MessageQueue的时候,会做个判断如果此msg的when小于表头msg的when时,那么将这个msg置为表头,否则就遍历链表,将此msg插入到最近的位置。这段操作相当于是将msg链表置为按when有序的,从表头到表尾按照when由小到大的顺序。所以这就是为什么looper始终取msg表头的那个msg来处理也可以执行到延迟msg的原因。

那么你肯定要问了

问题0.1:Looper取msg来处理时没到msg指定的延迟时间怎么办呢?

image.gif

如果还没到达时间,就让线程休眠,休眠的时间就是这个消息设置的处理时间距离现在还需要的时间,见上图。

问题0.2:线程休眠了那么其他的msg怎么处理呢?

忘记了吗?前面说的,msg链表是有序的,其他的msg肯定是比这个消息还要更后执行。

问题0.3:如果休眠这段时间来了一个新消息且when更小,那还会执行吗?

image

加上图是会执行,因为在向MessageQueue添加一个消息时会触发唤醒操作,就将线程唤醒再取消息出来处理。

问题0.4:如果表头的msg延时1秒,下一个msg延迟1.5秒,但处理表头的消息需要3秒时间开销,那么后一个msg还能按时执行吗?

是不会按时执行的,因为handler是一个消息一个消息处理的,所以尽量不要发送耗时的消息哦,容易引起anr不说,还会出现这类问题。

问题1:message是怎么被处理?

Looper被调用loop方法后(主线程自动,其他线程手动),会开启死循环去取消息交由handler处理。在取消息的方法里,会先判断是否开启同步消息障碍

问题1.1:什么是同步消息障碍?为什么要有同步消息障碍?

image

一般除非我们手动开启,不然我们发送的消息都是同步消息。在取消息时会先判断是否开启了同步障碍机制,见上图,开启了同步障碍后会优先取异步消息处理,不管队列中有多少个消息在等待(这里就有点霸道了,为什么呢?)。

问题1.2:前面我们看到队列是有序加入的,大家消息的when都是0,凭什么你开启同步消息屏障的消息就能插队呢?

image

接问题1.1,这样就能实现消息的优先处理,其实也就是简单的为消息添加了个优先级,异步消息优先执行。目的是为了让需要马上就处理的消息能够快速的被处理。

image image

再接问题1,Looper取到消息后交由msg的target进行分发处理,msg的target就是发送此消息的handler,交由发送此消息的handler处理,然后看到我们无比熟悉的handleMessage方法了。

问题2:如何做到的跨线程通信?

先抛出问题

问题2.1:如果多个线程同时加入msg,线程安全问题怎么处理?

每个Handler都有一个Looper,一条线程只能有一个Looper,Looper是线程私有的,由ThreadLocal来实现私有,一个Looper对应一个MessageQueue,也就解决了线程安全问题。也就做到了跨线程通信,因为不管在哪个线程发送的msg,使用的哪个handler就会发送到其持有的Looper的MessageQueue里。

问题2.2:Handler发送的msg发送到哪条线程去了?

由Handler持有的Looper决定,每个Handler实例化时都对应一个Looper,在主线程中创建的Handler默认用主线程的Looper,也可指定其他线程的Looper,使用哪个线程的Looper就把msg发送到哪条线程去处理。

至此Handler主要流程就理清楚了,那么有个探讨性问题,也是很多面试官关于这部分最喜欢问的问题。

问题3:Looper在主线程死循环为什么没有ANR?

先抛砖引玉,在梳理了Handler机制后,我认为这是一个伪命题。Looper死循环与ANR压根没有任何关系。首先Android应用是由消息进行驱动的,所以必须要有这个死循环不断的去取消息出来处理,如果没有这个死循环,那么程序无法进行下去(见ActivityThread的main方法,looper.loop()后面只有一行代码是抛出一个RuntimeException)。其次,与消息相关的ANR指的是在主线程处理的消息超过5秒没处理完(所有代码最终都是通过Handler消息机制来执行的),说的其实是Looper死循环体内的代码,与死循环本身无关。

所以我认为与Looper死循环相关的侧重点并不该是ANR问题,而是为什么死循环没有将CPU资源消耗殆尽?

除了没到延时消息处理的时间外,MessageQueue为空了也会触发线程休眠,没事情可处理了可不就要休眠么,释放了CPU资源,所以没有将CPU资源消耗殆尽。

新开的公众号,希望大家关注,不定期发布Android相关知识

52937C78-C7B4-448C-9CA7-171AAC17B4E0.png
上一篇下一篇

猜你喜欢

热点阅读