相信换过几次工作的人,都会有个小金库,比如我就有几张大的思维导图,每次面试都会拿出来过一遍,倒不是说临时抱佛脚,毕竟有些知识在平时的工作中很少用到,一般大一点的公司都会有自己的基础框架和各种组件库,用久了之后,一些原生的基础知识反而记得没那么清了。
这个系列的文章,重点不在原理分析,任何一个知识点要把它说透的话,展开都能写很长。文章的重点在于把一些重要的结论性的东西提出来,如果觉得已经掌握了的话就可以略过,如果觉得还有点模糊的话,可以再花点时间去温习一下。
第一篇讲消息循环,这个算是各大公司面试中出现频率最高的题目了。我们心中的脉络应该是:
MessageQueue => Looper => Handler => HandlerThread
1.MessageQueue
关于消息队列,下面这张图画得比较清楚:
实际上,Java层和native层各有一个MessageQueue,所以你既可以在Java层发消息,也可以在native层发消息。
Java层的MessageQueue里的消息是用一个单链表管理的,同时还包含一个叫mPtr的int值,这个值实际上就是native层MessageQueue的指针。它最重要的一个方法是next(),是一个死循环,会通过nativePollOnce()调用到native层的MessageQueue。
native层的MessageQueue则是通过epoll的方式,监听多个文件描述符。比如我们熟悉的input事件,VSYNC事件,都是通过addFd的方式加入到消息队列的监听列表中的。当Java层有消息要处理的时候,也会写一个特殊的文件描述符来唤醒native的消息队列。
那么问题来了,谁发的消息先处理呢?记住这个优先级:先处理native发送的消息,再处理文件描述符上的消息,最后处理Java层的消息。
从这里也可以看出ANR的根源:如果Java的消息处理比较耗时,就没有办法及时处理native层的input事件了。
2.Looper
Looper的作用:为线程添加MessageQueue。
也就是说,Looper里包含一个MessageQueue,并且会驱动整个消息循环。
第1步:Looper.prepare(),把Looper对象放到线程的TLS里,所以1个线程最多只能有1个Looper。
第2步:Looper.loop(),调用前面提到的MessageQueue的next()方法,启动消息循环。每个消息里包含一个target对象,消息会被分发给对应的target,这个target就是Handler。
3.Handler
Handler中包含当前线程的Looper和MessageQueue,因此可以直接发送Java层的消息到队列中。
发送消息有2种方式:postXXX()和sendXXX(),其实底层实现都是一样的,post方法会把回调函数包装到Message里,这样你就不需要重写Handler的handleMessage()方法了。
4.HandlerThread
一句话:内部包含一个Looper的线程