一、前言
本文主要阐述Android消息机制的原理而非使用,网上有很多的文章介绍了如何使用,如不熟悉请先搜索了解。
二、Message Handler Looper 原理(java部分):
基本原理:基于epoll和eventfd。
说明:
Message Handler 与 Looper 以及MessageQueue是java层消息机制的重要组成部分。
其中,Message是消息的实体,它可以被循环使用。
Handler是事件的发起人,它可以将消息发到MessageQueue中。
MessageQueue是消息队列,但是核心的处理部分也都在这个类中。
Looper是一个线程相关的对象,同时也是驱动部分,以保证线程可以一直处理消息。
众所周知,一个线程如果本身没有循环的话,那么它执行完run方法后也就结束了。
但是Looper.looper()里面存在for循环,这是线程不会终止的原因,也是消息机制能够生效的前提。
而looper循环中只做两件事,一是消息获取(从MessageQueue),二是消息分发(交给相应的handler处理)。
其中消息获取的方式是MessageQueue.next()方法。
进入到MessageQueue.next()方法后,发现里面也有一个死循环for(;;)
这一方面说明for循环里面的操作可能一次并不能成功,另一方面只有return或break才能退出循环。
可以看到,循环中只有两个return暗示了程序的执行过程,要么return message,要么return null.
return null 也就代表这个线程的消息机制能力结束。
循环中先注意到有一个native调用
nativePollOnce(ptr, nextPollTimeoutMillis);
暂时忽略,然后有一个if语句
它的判断条件是
if (msg != null && msg.target == null)
但是正常情况下msg都是有target的。所以先看下一段
如果消息不为null,那么会判断当前时间now 是不是超过它所期待的执行时间when。
如果超过的话,该消息应该处理,那么直接返回。
如果时间没到,看一看差值,然后设置nextPollTimeoutMillis为这个值。
如果消息为null,设置nextPollTimeoutMillis=-1
剩余部分先忽略。
这样的话,假如此处没有返回消息,将会进入下个循环。
不过此时nextPollTimeoutMillis有所变化,不是刚开始的0.
因为nativePollOnce是一个native调用,所以进到JNI层android_os_MessageQueue.cpp
对应的函数是
android_os_MessageQueue_nativePollOnce
可以看到,传进的第一个参数ptr转化成了NativeMessageQueue的指针(android_os_MessageQueue_nativeInit时生成)。
而它的pollOnce操作又调用了native的Looper的pollOnce。
然后就到了重点,注意Looper.cpp在system/core/libutils中(Android 6.0)。
结果又来了一个for循环.....
进入到pollInner中
与上层有关的就一句
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
这里利用了epoll机制,其中参数timeoutMillis是上层传过来的nextPollTimeoutMillis.
如果timeoutMillis小于0,这个函数会一直阻塞在这里直到mEpollFd管理的文件描述符的内容有变化。
如果timeoutMillis大于等于0,会在timeoutMillis后返回(如果这段时间没有什么事情发生的话)。
注意mEpollFd是epoll机制本身的描述符,而Looper创建时添加了一个唤醒描述符mWakeEventFd,
它是通过eventfd创建的专门事件通知的描述符。
这样的话只需向mWakeEventFd里面写文件就可以唤醒epoll_wait函数。
底层提供的调用是wake(),上层也可以通过nativeWake调用来调用。
如此一来便实现了阻塞与唤醒,Android的消息循环得以实现。
补充说明:
上述说明有意的忽略掉了不常用的部分,原理就是我们常用的mHandler.sendMessageXX所发送的消息的处理流程。
但是,上层消息机制还提供了一些其他的机制。
1.障碍机制:添加了一个障碍后,在这个障碍之后的(when)同步消息则不会被执行(会在移除障碍后执行),如图一个栅栏一样,只允许异步消息(isAsynchronous)执行。
上面说的if (msg != null && msg.target == null),其中target为null就代表它是一个障碍。当然,你可以随时添加和移除。
2.IdleHandler机制:你可以添加一个IdleHandler,它会在没有消息或当前时间小于下一个消息的when.来执行IdleHandler的任务queueIdle。
目的就是让线程闲置的时候也可以执行一些不重要的任务。
附:
如何保证对象是线程相关的(也就是不同线程使用的是不同的对象)?
使用了ThreadLocal类。
为什么next方法里加了锁synchronized?
当然是因为多线程,Message是通过链表的方式形成的消息队列,而next操作(执行线程)和enqueueMessage操作(可能发生在别的线程)都会
引起Message队列的变动,所以加锁是合理的。
三、MessageEnvelope,Looper(C++部分)
原理:同Java部分
说明:与java部分不同的是,底层部分几乎一切都发生在Looper.cpp中。
其中MessageEnvelope的功能与java层的Message类一样。
注意:应用开发可能无法使用Looper.cpp因为此部分并没有公开,但是NDK提供了另一种方式也可以进行通信,
即下面的ALooper。
四、ALooper (NDK开发)
原理:同Java部分
说明:应用开发者可以使用NDK提供的ALooper类来间接使用Looper,因为ALooper仅仅是对Looper的包装,并公开了部分接口。
其中add_fd接口是留给开发者可以使用的。
可以在frameworks/basse/native/android/looper.cpp找到Alooper的实现.
也可以在frameworks/native/include/android/looper,h或下载的NDK中的platforms/android-24/arch-x86/usr/include/android/looper.h
找到对应的头文件.
add_fd在Looper中的操作也就是
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
将该fd纳入到了epoll观测的范围,这样Looper的pollonce也会执行响应的操作。
总体来说,如果你已经理解epoll机制,理解上面的设计都是很容易的。
本文转自:博客园 - lightverse,转载此文目的在于传递更多信息,版权归原作者所有。