Android 消息机制 - 总结

1,002 阅读3分钟

源码基于 Android S AOSP,源码在线查看地址: cs.android.com/android/pla…

线程间通信在 Android 系统中应用十分广泛,本文是一个系列文章,主要梳理了Android 中 Java、native 层的线程间通信机制。

前几篇文章中,分析了一下Java层线程间通信Handler,并以一个简单的Handler示例开始,追踪了一下源码,源码分析期间,分析到Java层的Handler需要依赖native层实现wait,我们又分析了一下native层是如何实现wait的

紧接着,以system_server中InputReader与InputDispatcher两个线程为例,分析了Looper是如何实现InputReader唤醒InputDispatcher的。

Content

在Java层体系中,整体流程如下:

  • 线程中调用Looper.prepare()创建Looper并放在当前线程的ThreadLocal中,初始化MessageQueue
  • 线程中调用 Looper.loop(),开启死循环,不断的polling MessageQueue,有消息则取出处理(在message.target.dispatchMessage中处理),无消息则调用native层nativePollOnce实现wait
  • 创建Handler的时候会从当前线程的ThreadLocal中取出looper,在Looper中拿到MessageQueue
  • 调用Handler.sendMessage()会将message.target指向当前Handler(即上述message.target.dispatchMessage会到Handler中处理)
  • 将Message通过enqueueMessage()的方式加入到MessageQueue的链表中
  • 通过nativeWake的方式唤醒nativePollOnce

nativePollOnce整体流程如下:

  • 在Java层构建MessageQueue的过程中,会调用nativeInit创建一个native层的NativeMessageQueue,并返回句柄给java层的MessageQueue
    • NativeMessageQueue在构建的过程中会创建native层的Looper
    • native层的Looper在构建的过程中会创建好epoll
  • 调用nativePollOnce会从NativeMessageQueue调用到native的Looper.pollInner
  • Looper.pollInner内会调用epoll_wait实现等待,等待对端向fd写数据

nativeWake整体流程如下:

  • 通过NativeMessageQueue调用到native层的Looper.wake方法
  • Looper.wake向fd中写入整数1(写入的什么数据不重要,主要是要唤醒epoll_wait)
  • Looper.pollInner中的epoll_wait被唤醒

system_server中InputReader与InputDispatcher的间通信整体流程如下:

  • InputManager创建InputReader与InputDispatcher
    • 创建InputDispatcher的过程中会在InputDispatcher对象内部创建一个Looper(native)
    • 启动InputDispatcher线程
    • 构造InputReader的过程中会传入InputDispatcher,将InputDispatcher加入到QueuedInputListener
    • 启动InputReader线程
  • InputReader线程从EventHub接收到Event后,将Event push_back到QueuedInputListener容器内
  • 紧接着InputReader调用QueuedInputListener.flush方法,遍历容器内的Event,调用NotifyArgs.notify方法
  • 在NotifyArgs.notify方法内调用InputDispatcher对象(此时并不在InputDispatcher线程内,只是调用InputDispatcher对象)内的notifyKey方法
  • InputDispatcher.notifyKey调用InputDispatcher.mLooper.wake方法,唤醒InputDispatcher线程内的dispatchOnce,InputDispatcher线程被唤醒,开始工作。

从上述流程中可以看出,native层整体是依赖于native的Looper类实现的,而NativeMessageQueue只不过的Java层的MessageQueue到native层的Looper的一个过度,在NativeMessageQueue中并没有很重要的逻辑。

另外,在InputDispatcher与InputReader的线程通讯中,InputReader唤醒InputDispatcher是直接通过Looper实现的,并没有经过NativeMessageQueue,进一步验证了NativeMessageQueue只是Java层与native层的一个过度。

在Java层的Handler中,并没有采用Object类中的wait以及notify方法实现等待,而是采用linux中的epoll_wait模型实现,这么做的原因应该是为了执行速度吧,毕竟epoll_wait性能更好一点。

image-20220331161915928.png