Looper是android中很重要的概念,它是android application端线程间最主要的通信方式,同时它也是线程内部序列化处理的主要方式,Looper的核心其实是一个消息队列,通过不停的处理Looper消息队列中的消息来完成线程间的通信和线程内部序列化操作。任何线程想要使用消息机制特定的操作,那么必须在线程中创建一个Looper,java端的Looper如何使用不介绍了,所有有过android开发经验的人都知道怎么用,这篇文章主要介绍一下Native Looper,看它如何和JAVA层的Looper相互配合完成android中最主要的线程通信机制的。同时写这篇的目的是为了解释文章中事件传递过程中,native Looper是如何被复用实现管道通信的。
上面说到JAVA Looper的核心其实是一个消息队列,并且我们分析一下Looper.java的代码,并没有任何和Native有关联的数据结构和操作,那么唯一能和Native Looper发生联系的就剩下这个JAVA的消息队列类型MessageQueue了,也果不其然,在MessageQueue中定义了一个成员
它保存着对应的Native的消息队列实例的地址,用一个int类型的成员保存native实例,这是jni开发中常用到的方式。因此MessageQueue同样使用mPtr来表示native的消息队列,NativeMessageQueue@android_os_MessageQueue.cpp,看一下NativeMessageQueue的构造函数,其中定义了一个Native的Looper,
- NativeMessageQueue::NativeMessageQueue() {
- mLooper = Looper::getForThread();
- if (mLooper == NULL) {
- mLooper = new Looper(false);
- Looper::setForThread(mLooper);
- }
- }
因此整个的结构很简单,JAVA Looper包含一个MessageQueue,MessageQueue对应的Native 实例是一个NativeMessageQueue实例,NativeMessageQueue在创建的时候生成一个Native Looper。
其实Native Looper存在的意义就是作为JAVA Looper机制的开关器,
1. 当消息队列中有消息存入时,唤醒Natvice Looper,至于如何发送向线程消息,这就用到了Handler,google的文档中说明的很详细,不介绍了;
2. 当消息队列中没有消息时或者消息尚未到处理时间时,Natvice Looper block住整个线程。
上述功能其实一句话就可以总结:创建了JAVA Looper的线程只有在有消息待处理时才处于活跃状态,无消息时block在等待消息写入的状态。
下面我们就来分析Natvice Looper它是如何实现这个功能的,先从Natvice Looper入手,看它能干什么。
1. Native Looper初始化
我们看一下Natvice Looper的初始化过程都作了那些工作
1. 创建一个pipe管道mWakeReadPipeFd和mWakeWritePipeFd,这个管道的作用就是为了将block住的线程唤醒。
2. 创建一个epoll实例mEpollFd,用它来监听event触发,event有mWakeReadPipeFd上的 wake event;还有上一篇文章当中讲到的NativeInputQueue和InputDispatcher模块注册在各自Looper中,需要监听的的硬件设备的事件发生时的通知event以及事件消化完的通知event。
在构造函数中只向mEpollFd添加对mWakeReadPipeFd的监听,NativeInputQueue和InputDispatcher模块注册的监听需要通过addFd()方法在各自的模块中注册,目前来看只有NativeInputQueue真正使用到当前线程的Native Looper,而InputDispatcher是自定义了一个Native Looper,参考上一篇文章。
- Looper::Looper(bool allowNonCallbacks) :
- mAllowNonCallbacks(allowNonCallbacks),
- mResponseIndex(0) {
- int wakeFds[2];
- int result = pipe(wakeFds);
- LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
-
- mWakeReadPipeFd = wakeFds[0];
- mWakeWritePipeFd = wakeFds[1];
-
- result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
- LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d",
- errno);
-
- result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
- LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
- errno);
-
- #ifdef LOOPER_USES_EPOLL
-
- mEpollFd = epoll_create(EPOLL_SIZE_HINT);
- LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
-
- struct epoll_event eventItem;
- memset(& eventItem, 0, sizeof(epoll_event));
- eventItem.events = EPOLLIN;
- eventItem.data.fd = mWakeReadPipeFd;
- result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
- LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d",
- errno);
- #else
- ...........................................
- }
2. Native Looper的业务逻辑
这一部分我们深入分析一下Native Looper作为JAVA Looper机制的开关控制器的。
JAVA Looper通过调用loop()不断的去检测消息队列中是否有消息需要处理,调用的是MessageQueue的next()方法,这个方法返回的是MessageQueue的存储的消息。
loop()@Looper.java
- Message msg = queue.next();
- final Message next() {
- int pendingIdleHandlerCount = -1;
- int nextPollTimeoutMillis = 0;
-
- for (;;) {
- if (nextPollTimeoutMillis != 0) {
- Binder.flushPendingCommands();
- }
- nativePollOnce(mPtr, nextPollTimeoutMillis);
-
- synchronized (this) {
-
- final long now = SystemClock.uptimeMillis();
- final Message msg = mMessages;
- if (msg != null) {
- final long when = msg.when;
- if (now >= when) {
- mBlocked = false;
- mMessages = msg.next;
- msg.next = null;
- if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
- return msg;
- } else {
- nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
- }
- } else {
- nextPollTimeoutMillis = -1;
- }
-
-
- if (pendingIdleHandlerCount < 0) {
- pendingIdleHandlerCount = mIdleHandlers.size();
- }
- if (pendingIdleHandlerCount == 0) {
-
- mBlocked = true;
- continue;
- }
-
- if (mPendingIdleHandlers == null) {
- mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
- }
- mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
- }
-
-
-
- for (int i = 0; i < pendingIdleHandlerCount; i++) {
- final IdleHandler idler = mPendingIdleHandlers[i];
- mPendingIdleHandlers[i] = null;
-
- boolean keep = false;
- try {
- keep = idler.queueIdle();
- } catch (Throwable t) {
- Log.wtf("MessageQueue", "IdleHandler threw exception", t);
- }
-
- if (!keep) {
- synchronized (this) {
- mIdleHandlers.remove(idler);
- }
- }
- }
-
-
- pendingIdleHandlerCount = 0;
-
-
-
- nextPollTimeoutMillis = 0;
- }
- }
在获取MessageQueue中消息过程中,我们发现next()是一个循环,除了获取消息队列之外,最重要的一点就是去监听Natvie Looper的event触发,调用nativePollOnce(mPtr, nextPollTimeoutMillis); 它最终会调用到pollInner()@Looper.cpp。我们来看看pollInner都干了些什么?
1. 等待mEpollFd的事件触发,我们前面说过,这个事件触发有两种,第一个就是唤醒Native Looper的wake消息,另外一个就是复用Native Looper的其他消息,如NativeInputQueue和InputDispatcher的管道检测(目前android也就这两个模块使用到了)。
当epoll_wait()的等待时间不为0时,即JAVA Looper传递下来的nextPollTimeoutMillis值,那么整个线程就被block在这儿了。
pollInner()@Looper.cpp
- struct epoll_event eventItems[EPOLL_MAX_EVENTS];
- int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
- bool acquiredLock = false;
2. 如果有事件触发发生,wake或者其他复用Looper的event,处理event,这样整个Native Looper就从block状态中解脱出来了,整个线程也就解脱出来了,JAVA Looper则会执行nativePollOnce(mPtr, nextPollTimeoutMillis);下面的语句,next()@MessageQueue.java后面的语句就不解释了。
我们只需要注意nextPollTimeoutMillis值的设置,如果消息尚未到达处理时间,则nextPollTimeoutMillis值则为距离该消息处理时间的总时长,表明Native Looper只需要block到消息需要处理的时间就行了。如果没有消息待处理,那么将一直Native Looper将一直block住,等待wake event。
那么什么时候会有wake event发生呢?只有在有新的消息被存储到MessageQueue时,会向Native Looper发起wake event。
enqueueMessage()@MessageQueue.java
- if (needWake) {
- nativeWake(mPtr);
- }
整个Native Looper的基本机制就是这样的,保证在线程无消息可处理时,能够尽可能的减少CPU的利用,将宝贵的CPU资源交给其他线程或者进程处理,这一点在移动设备中是很重要的。
3. Native Looper扩展应用
Native Looper尽管通常情况下是和JAVA 层的Looper和MessageQueue配合使用的,作为JAVA Looper的开关控制器存在的,但是鉴于上面我们对Native Looper的分析,我们发现在native code开发时,我们完全也可以单独使用Native Looper,比如说在开发的native code中如果使用到了pipe或者socket通信的话,Native Looper将会是把利器,我们通过它能够很好的管理pipe或者socket等的通信。正如InputDispatcher所做的一样。