Handler作为Android系统最基本的消息处理机制,管理着整个应用程序的运行。应用程序中的任何操作包括:用户的点击事件,触摸事件,页面跳转等等,都会作为Message进入消息队列,最后被Handler处理。可以说Handler负责整个系统的运行。如果当系统中没有任何消息需要处理的时候,消息队列一直轮询会消耗CPU资源。那么Android系统是如果避免CPU浪费资源的? 我们接着往下看。
今天要给大家介绍的就是Handler底层的epoll机制。在学习epoll机制之前,我们先了解一下Handler基本的消息处理机制,如下图所示:
Handler里面维持了一个消息队列MessageQueue,通过Looper不断的插入消息,取出消息。代码如下。(MessageQueue.java)
当从MessageQueue中取出消息时先对检查消息执行的时间,如果时间还没到。则调用nativePollOnce方法使整个消息队列进入睡眠状态。
private native void nativePollOnce(long ptr, int timeoutMillis);
nativePollOnce方法是native方法,仅供jni调用,为了进一步Handler的底层实现原理,我们以Android10源码为例,进行深入理解。
当MessageQueue中没有Message需要处理或Message时间还没到时,调用java层的nativePollOnce方法。对应的JNI层中的 android_os_MessageQueue_nativePollOnce, 在JNI的android_os_MessageQueue_nativePollOnce中调用nativeMessageQueue->pollOnce(env, obj, timeoutMillis), 并传入对应的时间毫秒值。
我们接着往下看NativeMessageQueue里面是怎么处理的?
NativieMessageQueue.cpp
在NativeMessageQueue.pollOnce中调用了mLooper->pollOnce。进入Looper中。
Lopper.cpp
根据源码可以看出最后进入Looper.pollOnce的方法。在pollOnce中通过pollInner最终调用到epoll_wait,至此可以得出,Java层的nativePollOnce最终调用到JNI层的epoll_wait方法,并在在分析的过程中发现,JNI里面也有Looper。其他Java层的线程最终都是通过JNI调用pThread(感兴趣的同学可以查看JNI源码),对应的JNI也有和Java类名一样的线程相关类。
分析到这里,我们看到最终掉到JNI中的epoll_wait方法,那么这个epoll_wait到底是什么呢?我们接着往下看。
epoll是Linux内核中的一种可扩展IO事件处理机制,能够提高应用程序同时大量IO操作请求时的性能。首先我们要知道在Linux中一切皆文件,发送消息是一种文件IO的处理。Android操作系统也是基于LInux内核。那么系统是如何处理IO事件的呢?
一般当系统同时发起多个IO事件请求时,Linux需要轮询所有事件,处理每个事件对应的事件流。由于系统中链接Linux内核的IO事件实在是太多了,每一次轮询都需要耗费大量的时间和资源。
使用epoll_wait机制后。epoll会把每一个流对应的IO事件通知内核,这样内核就能根据具体的IO事件去处理对应的stream流。
当Java层调用nativePollOnce时,对应JNI调用到epoll_wait方法,等待系统的返回。使整个消息队列进入阻塞状态。一旦时间到了,epoll_wait主动返回对应的事件流交给Linux内核处理。或者插入新的Message通过调用nativeWake方法,调用Looper的wake方法,发送唤醒信号给Linux内核。
通过以上源码分析,我们可以看出Handler底层使用的epoll机制类似后端同学使用的Nginx服务器。关于epoll更深入的知识。