注:如下内容基于Android API Version 27(Android 8.1)Linux Kernel 3.18.0linux
Handler
、Looper
和MessageQueue
组成了Android的消息循环系统。消息循环系统是Android App的神经中枢,不管是与AMS/WMS
打交道,仍是UI绘制,亦或是手机输入事件的派发都依赖于消息循环系统。android
Android的消息循环运行于底层,咱们在上层开发遇到的一系列的组件生命周期回调,好比Activity
的onCreate
和View
的onTouchEvent
,都是从主线程的消息循环(Looper.loop
通过层层调用过来的。咱们在这些生命周期里填写代码最终造成了可使用的App,而这一切都是被底层的消息循环驱动着,所以咱们能够说Android App是一种基于消息驱动模型的应用程序。git
消息队列,用于存储和获取消息,包括Java层消息、Native层消息和各类文件描述符(FD)就绪消息。github
消息循环,内部包含一个MessageQueue
对象,经过一个无限循环的loop方法,持续从MessageQueue
获取并处理消息。socket
Java层帮助类,内部包含一个Looper
对象,Handler
经过Looper
中的MessageQueue
往Java层的消息队列发送消息。Handler
同时提供了消息处理的回调函数,Handler
收到消息回调后再决定将消息派发到哪里去。函数
消息循环起始于Looper
的loop
方法调用,此方法内部是一个无限的for
循环,线程一旦进入loop
就再也出不去了,除非主动退出。之后线程所作的事就三件:取消息、执行消息处理函数和等待,而线程的全部有意义的工做都发生在消息处理函数中。oop
Android的消息循环同时兼顾了Java层和Native层,Jave层和Native各自维护者本身的消息队列,Native层同时还监控着一批FD
,FD
就绪也是做为一种消息进行处理的。消息循环首先尝试消费Native层的消息(包括普通消息和FD
消息),消费完后再消费Java层的消息,若是Java层没有可供消费的消息了,线程就会在下一次消费Native时层阻塞在Native的epoll_wait
调用上,epoll_wait
用于等待其所监控的FD
就绪,若是Java层发送了新消息或者Native层发送了新消息,再或者有FD
就绪了,线程就会被唤醒,从而消息循环就会继续处理消息,直到Java、FD
和Native都没有消息了线程就又再次陷入阻塞。 Java层的消息在Java层处理,Native层的消息在Native层处理,Java层的消息和Native层的消息的惟一联系是他们在同一个循环线程中处理,线程阻塞是发生在Native层,线程唤起是发生在Java层和Native层,线程阻塞的时长和当前是否有消息或者下一个消息的处理时间有关。布局
Java应用程序经过Handler
将消息发送给MessageQueue
,消息按照时间从先到后进行排序放入链表中,若是消息要当即被处理,也就是说没有延迟的消息,此时若是线程处于阻塞状态,Java层经过jni
调用Native层的接口唤醒线程,线程唤醒后会从Nativie的Looer.pollOnce
返回到Java层的nativePollOnce
,而后继续处理Java层的消息,消息处理完后根据是否还有消息或者下一个消息等待处理的延迟时间来决定Native层下次是一直阻塞仍是使用一个超时时间进行阻塞。大体流程就是:阻塞-发消息-唤醒线程-处理消息-阻塞。post
Java层的MessageQueue
对象持有一个Native层的MessageQueue
对象,Native层的MessageQueue
持有一个Native层的Looper
对象,Java层的MessageQueue
调用Native的nativePollOnce
最终调到Native的Lopper.pollOnce
。Native的Lopper
建立时会将本身加入到Native的线程本地存储中去(TLS
),以便之后在线程执行的任意地方拿到Looper
对象向Native消息队列发送消息。.net
Native层的Looper
使用eventfd进行线程的唤醒,eventfd
是linux系统调用,经过eventfd
建立一个FD
,而后将这个FD
和其余真正要监控的FD
加入到同一个epoll
监控列表中,当须要唤起线程时向这个FD
写入一个字符,这样epoll
检测到有FD
就绪就会从阻塞中唤醒。当Java层须要唤醒Native层的epoll
阻塞时只须要调用Native层的方法向event FD写入一个字符,这样epoll_wait
就返回了。
Native层的Lopper.pollOnce
从epoll_wait
返回后首先处理Native消息队列中的消息,Native消息队列的消息是经过Native Lopper.sendMessage
函数添加的,每个消息都绑定了一个MessageHandler
对象,处理消息就是调用MessageHandler
的handleMessage
函数将消息对象自己传给发送消息者。处理完普通消息后,Native Looper
接着处理FD
就绪的消息,每个FD
消息在添加的时候(Looper.addFD
)会关联一个LooperCallback
对象,Native Looper
就是经过回调FD
所绑定的LopperCallback
来由使用方本身处理FD
就绪的消息的。 处理完全部的这两种消息后Native Looper
就返回了,接着线程就走到了Java层的消息处理逻辑中去了。
咱们之因此在建立了Looper
的线程中的任意位置能够访问这个Looper
缘由是Looper
对象是借助ThreadLocal
存储的。ThreadLocal
即线程局部存储。Looper
内部声明了一个静态的ThreadLocal
对象,TheadLocal
内部是以线程对象为key,线程局部数据为value存储在一个类map的结构中的。只要线程调用Looper.prepare
将当前建立的Looper
对象存在ThreadLocal
中,之后在线程执行的任意位置均可以调用Looper.myLooper
将存储的Looper
对象拿出来,而且每一个线程对应各自的Looper
对象。
关于线程局部存储能够参阅:Android 线程局部存储ThreadLocal原理总结
AMS
和WMS
对App进程的回调从binder线程转到App主线程。vsync
信号的处理。ViewRootImpl
刷新布局基于vsync
信号,而vsync
信号是经过读取socketpair的FD
得到的,将socketpair
的FD
加入到Native的消息轮询中能够直接使vsync
的回调发生在主线程,从而避免了一次线程切换。FD
加入到Native的消息轮询中从而实现了消息直接于主线程处理。参考
blog.csdn.net/luoshengyan…
androidxref.com/8.1.0_r33/x…
androidxref.com/8.1.0_r33/x…
linux.die.net/man/2/event…
linux.die.net/man/2/socke…
chao-tic.github.io/blog/2018/1…