1. Android 消息机制概述
Android 消息机制是由 Handler
、Looper
和 MessageQueue
三者合作完成的,消息机制可以分为消息机制初始化
、消息轮询
、消息发送
和消息处理
4 个过程来理解,消息机制是基于 Linux 的事件轮询机制 epoll
和用来通知事件的文件描述符 eventfd
来实现的 。
消息机制初始化过程
是从消息轮询器 Looper 的 prepare()
方法开始的,当线程调用 Looper 的 prepare() 方法时,prepare() 方法会调用 Looper 的构造函数创建一个 Looper ,并放到线程私有变量 ThreadLocal
中。Looper 的构造函数中会创建一个消息队列 MessageQueue ,而消息队列的构造方法会调用 nativeInit()
JNI 方法初始化 Native 层的消息队列
,在 Native 层消息队列的构造方法中,会调用 Native 层 Looper 的构造函数初始化 Native 层的 Looper
,而在 Native 层 Looper 的构造函数中会调用 rebuildEpollLocked()
方法,在 rebuildEpollLocked() 方法中会调用 epoll_create1()
系统调用创建一个 epoll 实例
,然后再调用 epoll_ctl()
系统调用给 epoll 实例添加一个唤醒事件文件描述符
,到这里消息机制的初始化就完成了。
epoll
、select 和 poll 都是 Linux 中的一种 I/O 多路复用机制
, poll 和 select 在每次调用时,都必须遍历所有被监视的文件描述符,文件描述符列表越大,性能就越差。而 epoll 则把监听注册从监听中分离了出来
,这样就不需要每次调用时都遍历文件描述符列表了。创建 epoll 实例时,Linux 会创建一个 evnetpoll
结构体,这个结构体中有 rbr
和 rdlist
两个成员,rbr 是红黑树
的根节点,epoll 会用红黑树存储所有需要监控的事件 ,rdlist 则是存放着要通过 epoll_wait()
返回给用户的事件。
唤醒事件文件描述符
是一个 eventfd
对象,是 Linux 中的一个用来通知事件的文件描述符
,与 pipe 相比,pipe 只能在进程/线程间使用,而 eventfd 是广播式的通知,可以多对多。eventfd 的结构体 eventfd_ctx
中有 wqh
和 count
两个成员,wqh 是一个等待队列的头结点,类型为 __wait_queue_head
,是一个自带自旋锁
的双向链表
的节点,而 count 则是一个计数器
。
消息轮询过程
是从 Looper 的 loop()
方法开始的,当线程调用 Looper 的 loop() 方法后,loop() 方法中会调用 MessageQueue
的 next()
方法获取下一条要处理的消息,next() 方法中会通过 nativePollOnce()
JNI 方法调检查当前消息队列中是否有新的消息要处理,nativePollOnce() 方法会调用 NativeMessageQueue
的 pollOnce()
方法,NativeMessageQueue 的 pollOnce() 方法会调用 Native 层 Looper 的 pollOnce()
方法, Native 层 Looper 的 pollOnce() 方法中会把 timeout
参数传到 epoll_wait()
系统调用中,epoll_wait() 调用后会等待事件的产生,当 MessageQueue 中没有更多消息时,传到 epoll_wait() 中的 timeout 的值就是 -1
,这时线程会一直被阻塞,直到有新的消息进来,这就是为什么 Looper 的死循环不会导致 CPU 飙高,因为主线程处于阻塞状态
。当调用完 nativePollOnce() 方法后,MessageQueue 就会看下当前消息是不是同步屏障
,是的话就找出并返回异步消息
给 Looper ,不是的话则找出下一条到了发送时间的返回非异步消息。
消息发送过程
一般是从 Handler 的 sendMessage()
方法开始的,当我们调用 Handler 的 sendMessage() 或 sendEmptyMessage() 等方法时,Handler 会调用 MessageQueue 的 enqueueMessage()
方法把消息加入到消息队列中。消息 Message
并不是真正的队列结构,而是链表结构
。MessageQueue 的enqueueMessage() 方法首先会判断消息的延时时间是否晚于当前链表中最后一个结点的发送时间,是的话则把该消息作为链表的最后一个结点。然后 enqueueMessage() 方法会判断是否需要唤醒消息轮询线程,是的话则通过 nativeWake()
JNI 方法调用 NativeMessageQueue 的 wake() 方法。NativeMessageQueue 的 wake() 方法又会调用 Native 层 Looper 的 wake()
方法,在 Native 层 Looper 的 wake() 方法中,会通过 write()
系统调用写入一个 W
字符到唤醒事件文件描述符
中,这时监听这个唤醒事件文件描述符的消息轮询线程就会被唤醒
。
消息处理过程
也是从 Looper 的 loop()
方法开始的,当 Looper 的 loop() 方法从 MessageQueue 的 next() 中获取到消息时,就会调用 Message 的 target
的 dispatchMessage()
的方法,Message 的 target 就是发送消息时用的 Handler
,Handler 的 dispatchMessage() 方法首先会判断 Message 是否设置了 callback
回调 ,比如用 post()
方法发送消息时,传入 post() 方法中的 Runnable
就是 Message 的 callback 回调,如果 Message 没有设置 callback ,则 dispatchMessage() 方法会调用 Handler 或 Handler 的 callback 的 handleMessage()
方法,到这里消息处理过程就结束了。
另外在使用消息 Message
的时候,建议使用 Message 的 obtain()
方法复用全局消息池
中的消息。
2. 消息机制初始化流程
消息机制初始化流程就是 Handler、Looper 和 MessageQueue 三者的初始化流程,Handler 的初始化流程比较简单,而 Looper 的初始化流程则是从 prepare()
方法开始的,当 Looper 的 prepare() 方法被调用后,Looper 会创建一个消息队列 MessageQueue ,在 MessageQueue 的构造方法中会调用 nativeInit()
JNI 方法初始化 Native 层的消息队列,在 NativeMessageQueue
的构造方法中会创建 Native 层的 Looper 实例,而在 Native 层的 Looper 的构造函数中,则会把唤醒事件的文件描述符
和监控请求的文件描述符
添加到 epoll 的兴趣列表
中。