在Android系统主要的通信机制有binder机制和handler消息处理机制,前者多用于进程间的通信,后者则多用于进程内线程间的通信。其实,android进程就是基于消息驱动的。
一般而言,消息处理机制都应含以下几个要素:
在Android系统中上述要素大致对应为Message、Handler、MessageQueue、Looper。当然,从细节上来说Handler不止作为消息的发送方存在,同时还承担了消息处理的dispatch工作。在多数开发场景(主要是MainThread环境)中,得益于系统良好的封装性,开发者可在无感知Looper与MessageQueue的存在下只需一个Handler就可完成消息的投送以及处理,十分简单好用。也因于此,使得开发者难以一窥全貌,了解消息处理机制的核心所在。
那么handler消息处理机制都有什么特点呢?
在了解上述特点之后,有两个问题值得思考,1.如何保证线程与Looper为一对一关系?2.当前轮询器中如何在没有消息时让位CPU资源,实现阻塞式轮询?下边介绍的储备知识点就是解答这两问题之关键。
基于如何在多线程并发环境下,防止线程变量被其他线程打扰或篡改的问题,java早在1.2版本时就引入了Thread的本地存储技术。在当前线程某处set保存变量时,开发者可在该线程任意地方get取出变量,从而完成线程内部变量共享。
上述所说的线程本地变量都是保存在Thread内部成员变量localValues中。在下边我们贴了Thread类的部分代码,可见localValues是ThreadLocal.Values类型,Values实质上就是一个简化版的hashMap,其内部维护了K-V结构的可扩容数组,开发者通过Thread本地存储的变量都是以一定的映射关系按照K-V形式存放于此。当然,细心的朋友可以观察到inheritableValues同样也是K-V结构的,这里稍提一嘴,inheritableValues所存储的变量不仅线程内部可以访问,当前线程所创建的子线程同样可以访问(其对应的管理方为InheritableThreadLocal类)。
public class Thread implements Runnable {
...
/**
* Normal thread local values.
*/
ThreadLocal.Values localValues;
/**
* Inheritable thread local values.
*/
ThreadLocal.Values inheritableValues;
...
}
介绍完线程本地变所保存的位置后,接下来就是如何把线程变量本地化了。然而,通篇查找Thread的方法,却并无直接的set、get方式实现这个功能,这又是怎么回事呢?
线程变量本地化是通过本小节的主角ThreadLocal完成的。ThreadLocal是通过set/get方式从调用线程的localValues中存取其本地指定类型的变量,我们知道Thread的localValues类似是K-V结构的Map,如果开发者通过ThreadLocal存取变量,则K对应当前ThreadLocal的引用,V类型则由ThreadLocal的泛型指定。
在百度词条对ThreadLocal的定义中,其多次强调“ThreadLocal并不是一个Thread,而是Thread的局部变量”。我想这并不准确。ThreadLocal应该是作为线程指定类型的本地变量的管理者而存在,ThreadLocal与线程之间是一对多的关系,也就是说一个ThreadLocal具备管理多个Thread的localValues的能力。按此理解,我们经常见到ThreadLocal如下的定义方式(截自Looper类)也就见怪不怪了。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
在了解ThreadLocal的意义之后,我们从代码层面来看下。此类所拥有的方法不多,核心方法如下:
public T get();
public void set(T value);
public void remove() ;
Values initializeValues(Thread current);
既然ThreadLocal要负责管理各个线程的localValues,那么有一点很关键,它需要具备知道上述核心方法的调用线程的能力,这样才能做到精确打击。事实上,ThreadLocal也确实是这么做的。
我们以它的set方法为例,在下边代码中通过Thread.currentThread()定位到调用者当前所处的线程,而内部的value()方法即是从当前线程中取出localValues(本地变量存储区),如果为空,则还需初始化localValues。最后,则是通过values的put()方法把当前ThreadLocal和value作为K-V放入当前线程的本地变量存储区中。其实,如果您还愿意继续深究下put方法的话,你会发现K存放的是WeakReference
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
同理,ThreadLocal的get方法的操作与上边类似。并且从下可知所有通过ThreadLocal存储的线程本地变量都是保存在了Values的table数组中,并且第n个本地变量Value在table中的保存位置index与其所依赖的Key在table数组中的index角标为2n和2n+1的关系。如果通过get找不到调用线程的value,则通过values.getAfterMiss()返回一个空变量。
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
通过上边的说明,我想对ThreadLocal线程本地存储技术应该大致了解了。那么我们再来回答一下上边的问题1即如何保证线程与Looper为一对一关系。Android的消息机制就用到了ThreadLocal线程本地存储技术。我们知道ThreadLocal通过set/get负责管理调用线程的本地变量,即在set变量后,可在当前线程任意地方通过get取出该变量。那么我们就可通过get判断是否为null来确定当前变量是否set过。事实上,这也是下边的Looper.prepare()方法如何判断在同一线程只被调用了一次的根本。
先引用下百度词条对epoll的解释。
epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。
简单来说epoll就是为了提高CPU利用率,而对I/O操作的具体事件进行监听,当监听到事件发生时,会唤醒被阻塞的方法。
其具体可监听的有file、socket、pipe等I/O流。为了了解epoll的作用,我们拿pipe管道来打个比方。pipe是由内核管理的一块缓冲区构成,正如其名字那样,其分为输入(write)和输出(read)两端。由于输入端和输出端都采取轮询方式读或写数据,由于缓冲区大小受限,且两端轮询基于CPU利用率的关系都采用阻塞式轮询的方式。也就是说对于两端的代码工作者,都是基于有活就开始干活,没活了让出CPU资源方便其它程序使用。对于这种阻塞式轮询的方式,则需要某种机制来解除阻塞。类比于pipe读写两端,就对应于在缓冲区不空时,需通知输出端干活;缓冲区满时,需要通知输入端暂停干活;缓冲区空时,需要通知输出端停止干活;缓冲区不满时,需通知输入端开始干活。我们这里介绍的epoll就是为了解决类似pipe的这四种缓冲区事件监听的机制。
epoll共有以下三个相关的函数。
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
--> epoll_create 创建一个epoll对象,size用于告诉内核监听事件的最大数目。
--> epoll_ctl epoll对象的事件注册/删除/修改函数。此函数总计有四个参数,第一个参数用于告知事件注册的目的地,第二个参数则用于表明当前函数操作类型,第三个参数用于表述当前事件需要监听的fd对象,第四个函数则表示具体监听什么事件。
epoll操作类型有:
EPOLL_CTL_ADD:注册新的fd到epfd中。
EPOLL_CTL_MOD:修改已经注册的fd的监听事件。
EPOLL_CTL_DEL:从epfd中删除一个fd。
epoll事件类型:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的;
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。
--> epoll_wait 用于监听通过epoll_ctl函数所注册事件的发生,该监听是可阻塞式的,具体通过第四个参数timeout指定,如果timeout=-1,则当前epoll_wait会阻塞于此直到epoll_ctl事件所注册监听事件的发生;如果timeout=0,则即时返回监听结果;如果timeout>0,则指代epoll_wait函数至多阻塞的毫秒时间,该时间段内有监听事件发生,则返回监听结果。
通过上边的说明我们大概了解到了epoll的作用,下边则通过简单例子说明epoll_create、epoll_ctl、epoll_wait在程序中如何发生作用。在通过epoll_ctl监听事件后,epoll_wait每次都会阻塞式的等待监听事件的发生。
int main(int argc, char **argv)
{
//创建epoll
mEpollFd = epoll_create(MAX_EVENTS);
//监听事件
eventItem.events = EPOLLIN;
eventItem.data.fd = listenFd;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, listenFd, & eventItem);
while(1){
...
//等待事件发生-阻塞式
struct epoll_event events[MAX_EVENTS];
int fds = epoll_wait(mEpollFd, events, MAX_EVENTS, 1000);
//do something
...
}
return 0;
}
在Android消息机制中,当消息轮询器中发现没有消息时,就不能做非阻塞式忙轮询,而是需要在没有消息的时间段,或者消息未到执行时间之前的时间段中,把CPU资源让渡出去,并在消息到来时在唤醒轮询器,这就是上边所说的epoll技术在消息机制中所起的作用。
从概述中的消息机制要素的角度来看,一条消息的生命周期过程首先是从“消息的投送者”开始的。在Android消息机制中这个消息投送者的角色就是本小节要讨论的Handler。Handler作为作为消息投送者,它不光要知道投递什么消息,更关键的是它需要知道把消息投送到哪里去!这就好比快递员在送快递时需要知道该快递的地址一样。按此理解,在Handler中一定有一个投送目标,那么这个投送目标是什么呢?目标就是MessageQueue,每个Handler都需要知道自己需要把消息投送到哪个消息队列即MessageQueue中。这也解释了为什么Handler所有的构造方法中都直接或间接的需要Looper,因为MessageQueue是在Looper中创建的,是Looper的成员变量!只有拥有了Looper,Handler才能知道投送的目标。
所有的Handler构造最终都会调用以下之一。其中的mQueue就是消息最终投送到的地方。也许你还看到Handler把Looper也变成成员变量了,会思考Looper会不会也会在Handler中有不为可知的用处呢?很遗憾的告诉你,这个mLooper变量在Handler中就是个鸡肋,当然,这也是由于这也是由于Handler在整个消息机制中所处的角色决定的。
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
//默认false
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
消息发送是Handler必须具备的能力。在平时Handler的使用中我们一般都是用postXXX(Runnable)或者sendXXX(Message),这其实就是把消息发送到消息队列中等待处理。至于为什么会有Runnable和Message两种形式,其实两者本质上都是Message,只不过Handler内部对Runnable包装成了一条消息,具体而言就是以下方式包装。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}
可见,Runnable被包装到Message(即最终被投送的消息格式)的callback中。
无论通过postXXX()还是sendXXX(),最终都会走到Handler的sendMessageAtTime()方法中,由这个方法执行具体的消息投送工作,这个方法代码具体如下:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//最终都会通过此方法把Message入队
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
//就现有的非hide方法来看,此处进不去
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在sendMessageAtTime()方法中,我们可以看见最终消息确实被插入到消息队列mQueue中以等待轮询器轮询处理。这几行代码看似没几行,却包含了部分Android消息机制的重要思想。首先,在入队时指定了msg.target=this,这里其实非常重要,为什么呢?对于消息轮询器Looper来说,其并不关心被轮询到的Message具体的作用,消息具体作用是由消息本身内容决定的,这也就要求被轮询到的Message本身不光要知道自己从何处来(source),也要知道自己往哪里去(target),在上述enqueueMessage()中的msg.target指代为Handler消息发送者本身,也即说明了在消息在被轮询到后还应该回到Handler中来处理,正因如此,在Looper.loop()中当轮询到某条消息时会调用其msg.target.dispatchMessage()。其次,在消息插入MessageQueue时,传入了uptimeMillis时间,我们的消息队列就是据此对消息进行排序的。最后,在enqueueMessage中会根据mAsynchronous的值把当前所发的消息置成同步或者异步消息,关于同步异步消息的内容,详见后边关于SyncBarrier的讲解。
当Looper轮询器从消息队列中把某条消息取出后,会根据Message.target把此消息发给消息接收者来处理,这个target就是Handler本身,也就是说Handler不仅作为Message的发送者,还作为该Message最后的处理者。消息的处理是通过dispatchMessage进行的。
public void dispatchMessage(Message msg) {
//callback本质上是一个Runnable,在handleCallback中调用runnable.run()
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
//此处通过Callback接口方法的返回值决定是否继续传递
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
在dispatchMessage中,对在发送时包装的Runnable与Message进行了分解处理。如果是Runnable(保存在msg.callback中),直接调用Runnable的run()方法;如果是Message,则先检查在创建Handler时是否传入了Handler.Callback,如果存在,则视Callback方法返回值决定是否打断消息传递。可以看见对于Message,消息处理最终放入了handleMessage()方法中,这也是为什么我们通常在创建一个Handler时需要重写其handleMessage()方法的关键所在。
另外,在Handler中,还拥有一些辅助方法来移除/查询消息队列中的Message的功能,这里就不展开说了。
Looper即轮询器,它是Android的消息机制能够运行的核心所在,其内部拥有一个MessageQueue消息队列,外界可通过构造Handler往此队列中投送消息,而Looper依靠阻塞式轮询方式把这些消息取出进行后续处理。但是Looper却是依赖于具体的线程,篇首有说过,Looper因为其使用for(;;)语句不断轮询的特点,所以一个线程至多拥有一个Looper,而而这种线程与Looper之间一对一的关系又是依赖于之前介绍的ThreadLocal本地存储技术来保证的。Looper到底怎么运作,我们来看下边代码介绍。
此方法是类方法,用于使用ThreadLocal技术在此方法调用线程构造一个唯一的Looper对象。
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//同一线程中prepare只能被调用一次
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//调用prepare的目的是往ThreadLocal中保存Looper实例,方便在当前线程任意地方取出
sThreadLocal.set(new Looper(quitAllowed));
}
通过prepare方法阅读可以发现,这个方法在同一个线程中是不能被多次调用的,多次调用会抛出Exception。同时为了方便理解,我们也把Looper具体存储管理者sThreadLocal给贴出来了。当然,在该线程任意地方都可以通过以下方式取出唯一Looper。
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
* 在哪个线程调用就取出次线程的Looper,具体逻辑在ThreadLocal的get方法中
*/
public static Looper myLooper() {
return sThreadLocal.get();
}
还有一个问题在这里需要解答下,为什么在平时我们对消息机制的使用过程中,绝少使用Looper.prepare(),这是因为大多数场景都是在主线程进行的。而主线程的Looper由系统自行创建好了,其别有一个名称叫MainLooper,这个MainLooper并保存在Looper类成员sMainLooper中,基于类成员在进程死亡之前一直存在,所以我们可以在进程任何地方通过Looper.getMainLooper()来获取到它,进而通过Handler往主线程的消息队列投递消息轮询处理。
当你看到某个线程调用了Looper.loop()方法时,说明这个线程已经进入事实上的轮询,除非调用quit/quitSafely退出。很明确的说,这个方法的调用依赖于prepare在当前线程事先被调用过。
public static void loop() {
//取出保存在ThreadLocal中的Looper实例中的MessageQueue(消息队列)
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//循环体,用于不停循环队列中的消息
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//target是保存在消息中的Handler引用,此处的dispatchMessage所在线程为loop方法调用所在线程
msg.target.dispatchMessage(msg);
final long newIdent = Binder.clearCallingIdentity();
//消息在发送完之后自动回收到保存在Message中的pool中
msg.recycleUnchecked();
}
}
loop()是一个类方法,正因此,在内部首先需要检查其调用线程的Looper对象是否已经创建,否者无法开启轮询。当检查完后就处于死循环的过程中,在每一次循环时,都试图通过mQueue.next()方法(强烈注意!这个next是阻塞式的)从Looper唯一存在的消息队列MessageQueue中取出一条消息进行处理,具体怎么处理前边有分析过,就是通过在消息投送时保存在msg.target的Handler来进行剩余的dispatch部分工作;最后当消息处理完时,通过调用当前轮询到的消息的recycleUnchecked()来进行回收。
会什么会有这个消息回收呢?这是基于消息复用以减少内存消耗的需要。至于回收到哪里去,回收的消息又如何再利用。这里先不展开,我们在下个小节会详细介绍。
MessageQueue主要是完成Android消息机制中未处理消息的保存及排序等需要,需要具备基本的插入消息、读取消息的功能。基于单个消息节点创建以及删除方便的考虑,MessageQueue内部维护了一个mMessages单向链表来作为其消息结构。消息作为单链表中的节点,其内部需要维护下一个消息节点的引用。当调用MessageQueue的插入、查询、删除消息等操作时,其实就是对其内部这个单链表进行操作。
public final class Message implements Parcelable {
...
// sometimes we store linked lists of these things
/*package*/ Message next;
...
}
消息队列MessageQueue是在new Looper对象的时候创建的,这也说明了在篇首时我们描述的Looper与MessageQueue是一对一的关系。在消息队列创建时,其同时调用了native层的nativeInit()方法,此方法具体在android_os_MessageQueue.cpp中,nativeInit最终调用到NativeMessageQueue类的构造方法。
NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
从以上代码可知在MessageQueue创建时,在native层也构建了一套Android消息机制以专门为当前程序的native层服务。当然,由于本人C++水平有限,这里暂时不对native层的消息机制铺开来说了。
在之前介绍Handler时曾强调其最终是将经过包装的Message插入到创建Handler时从当前线程所获取到的mQueue中,具体调用的就是消息队列mQueue.enqueueMessage()方法。
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
//因为插入队列这个过程可能在不同的线程调用到,为链表增删的安全性,所以此处需要加锁
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
//如果链表为空或者当前插入的Message的when要小于链头的when,则把当前msg作为链头
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//根据when排队,when的值越大越往后,如果when值相同,则在同when最后边
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
//在插入数据时,如果当前线程处于休眠态,或者当前消息是一个异步消息,则需要唤醒当前线程
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
enqueueMessage内部首先对消息的合法性进行了一些判断,如果当前需要插入的消息合法,则根据消息的when值插入到mMessages链表中合适的位置。在插入完成之后,就如同生产者消费者模式那样,如果消费者next进入了mBlocked=true即阻塞状态,则生产者enqueueMessage还需要根据实际情况选择去nativeWake唤醒消费者,那么什么时候我们需要唤醒被阻塞的next方法呢?从上边代码可以看出,会根据插入消息的when时间,或者消息是否是异步消息决定去wake被阻塞的方法。
且基于对mMessages的保护所有可能影响到mMessages链表的操作都被加上同步锁MessageQueue.this,以防止其被意外篡改。
关于nativeWake是如何唤醒被阻塞方法next,以及next被阻塞的条件如何?在下边分析。
在Looper.loop()开启轮询后,由MessageQueue的next方法负责从其内部的单链表mMessages中取出被轮询到的消息,但如果链表中没有消息的话,那么取消息的过程就变成了一个阻塞的过程,会一直阻塞直到当前链表中被插入一条消息。
next()是整个Android消息机制实现阻塞式轮询的关键之处,但由于其冗长的代码段,也让人望而生畏。下边就是next方法的整个过程,之后我们会有详细的解答。
//核心方法,阻塞式调用
Message next() {
//第1部分
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
//第2部分
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
//第3部分
//由此可见next方法时阻塞式的调用
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//第3.1部分
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
//第3.2部分
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//如果发现当前的消息是SyncBarrier消息(通过target==null判断),则取出消息队列中的异步消息进行处理,而阻塞掉大部分的同步消息
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//第3.3部分
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (false) Log.v("MessageQueue", "Returning message: " + msg);
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
//第3.4部分
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
//第3.5部分
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
//不能因为IdleHandler调用崩溃导致looper退出循环
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf("MessageQueue", "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
从整体的角度来看,next方法无非就是从消息队列中取出当前符合条件的消息返给轮询器Looper,并在没有消息时阻塞于此以让出CPU资源,并且通知出去Looper阻塞了,大家伙有事的赶紧操起家伙干活,不然一会我忙起来了你们还得排队等CPU资源。这就是next方法的整体套路,但是还有许多细节值得研究,在next代码段里,我们把其分成了若干部分,接下来我们就逐一解读。
第1部分:用于判断当前消息队列是否已经退出了(具体退出是通过调用MessageQueue.quit()方法),如果已经退出了那么返回null以便Looper退出轮询。这个quit退出的方式有两种,分为安全或不安全,安全是指当前队列可以继续把那些msg.when<=now的消息执行完毕才退出,而不安全则就是暴力清空队列。当然,在一般情况下程序不会主动quit,特别是MainThread,因为quit就代表当前线程已经gameover了。
第2部分:很简单,就是对两变量进行初始化,但若是结合整个next来看,这两个变量又有着深刻的含义。nextPollTimeoutMillis则表示当前需要阻塞的时间,默认0表示无需阻塞,-1表示一直阻塞,大于0则表示具体阻塞的超时时间。pendingIdleHandlerCount用于记录当next处于阻塞状态时需要通知的“人数”。开发者可以通过MessageQueue的addIdleHandler和removeIdleHandler来进行添加。其实系统就是用IdleHandler的这种机制来做GC回收。
//frameworks/base/core/java/android/app/ActivityThread.java
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
return false;
}
}
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
Looper.myQueue().addIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
第3部分:进入next的阻塞式调用直到得到一条Message,这个“直到”就是通过for(;;)循环保证,当找到消息时才会跳出循环。
--> 第3.1部分:nativePollOnce是一个native层方法,是一种可阻塞式的调用。其有两个参数,参数1是native层消息队列指针(即NativeMessageQueue),参数2则是具体阻塞的时间。关于此处如何阻塞,阻塞时间及如何唤醒,可参见下边epoll在MessageQueue中的使用。
--> 第3.2部分:此部分是在nativePollOnce阻塞方法被唤醒之后调用,主要是在唤醒之后试图从消息队列中获取一条可用消息。请注意,此部分进入的条件是消息队列mMessages不为空且链表首条消息的msg.target=null才会进入。条件之一的消息队列不为空是很容易理解的,如果消息队列中没有消息,那就不用试图找了。条件二msg.target=null则需要结合下部分SyncBarrier一起才会更好理解。这里先提一嘴,msg.target=null是同步栅栏的标志,当链首消息是同步栅栏SyncBarrier时,则意味着此消息之后的所有同步消息(msg.isAsynchronous()用于判断当前消息时同步还是异步)都不能取出来。
--> 第3.3部分:当3.2部分试图取出一条消息后(可能为空)需要判断这条消息是否可用。这部分代码其实很明朗,有消息的情况下,通过msg.when判断当前消息是否到了执行时间,如果到了立即返回,如果还没到,则之后会continue循环到上边的nativePollOnce方法阻塞在这里计算出来的时间nextPollTimeoutMillis;在没有消息的情况下,同样之后会continue继续循环到nativePollOnce方法,只不过这时的nextPollTimeoutMillis=-1,也就是不设超时的阻塞,除非调用wake唤醒。
--> 第3.4部分:此部分则是判断当前是否已经调用了MessageQueue.quit()方法退出。如果mQuitting=true则调用dispose把native层的引用计数-1。这里有个关键问题,当quit方法被调用时,无论是否是安全quit,都会把msg.when>now的消息从链表中删除掉。但是这里我有个疑问,如果在调用quit。之前有加入同步栅栏,那么msg.when --> 第3.5部分:如果进入到此部分则说明3.2部分并没有取出到消息或者取出的消息未到执行时间,所以在下次循环时会进入阻塞状态,3.5部分则是在进入下次循环之前把mIdlehandlers集合遍历通知一遍:“Looper轮询进入阻塞状态,暂不使用CPU资源,大伙可以开始干活了”。 在next方法中,mBlocked变量就是用于标识当前轮询是否阻塞。这也是enqueueMessage方法中唤醒时依据的准绳之一。 在绝大多数开发场景中,同步栅栏都不会被使用到,正因如此,SyncBarrier这个词对我们而言是如此的陌生。首先我们需要了解,Handler也就是消息的投送者其实分为剑宗和气宗两个派别!呃,跑偏了。但是Handler确实有两种不同的派别,两者各自所具有的的能力是稍有差别的,这其中主要体现在两者发送的消息类型不一样,分为同步消息和异步消息两种。在一般情况下,我们所创建的Handler都只具备发送同步消息的能力。这是由于具有异步消息能力的Handler投送者Google并没有对我们开放。下边我们我们再贴一遍Handler的部分构造方法。 在构造方法中mAsynchronous变量就是标识当前Handler是发送同步消息还是异步消息的Handler。如果去查阅Handler所有构造方法你就会发现,所有 与boolean async参数相关的非hide构造中都是false的,也就是说是同步的。这也说明发送异步消息这种功能“一般人”不能拥有。当然,如果想拥有一个发送异步消息的Handler,使用java反射技术也是可以的。 在聊完了Handler的两种派别所发送的消息还分同步与异步之后,SyncBarrier就该闪亮登场来了,SyncBarrier说到底其实也是一条Message,它主要是为充当栅栏一样的功能,也就是说单链表中在SyncBarrier之后的同步消息,在这个栅栏移除之前是无法被轮询器轮询到的,此处注意!是同步消息无法被轮询,如果是异步Handler发过来的异步消息则是可以被轮询处理的。几乎,面对SyncBarrier的超能力,我们能接触到的Handler都有那么丢丢无力感。下边就是SyncBarrier发生作用的地方。 那么怎样区分一条消息是不是同步栅栏,以及怎么添加和删除这个同步栅栏呢? 同步栅栏有一个明显的特点,即msg.target=null。这其实也不难理解,普通的消息是需要知道他从何处来,往何处去的,但是同步栅栏不一样,它的职能早就由Android消息机制分配好了,其发生作用也是由Android消息机制本身决定的,没必要也不能把其分配给某个Handler来处理。正因如此,在上边的代码中这个if (msg != null && msg.target == null)判断成立时,就说明单链表的链首是同步栅栏,所以这时候它后边所有的同步消息都被跳过了。 很容易想象,如果同步栅栏并不依赖于某个具体的Handler(msg.target)。那么其添加和删除过程应该也不是通过Handler来完成的。同步栅栏是通过Looper对象完成添加/删除的,同样,这两也有hide标记,说明其在一般情况下不提供给普通开发者来使用。在下边方法中,需要注意的是,在添加一个SyncBarrier时会返回一个token,这就是删除SyncBarrier是所需要的令牌,这就好比古代兵符一样,兵符匹配上了才能发兵。需要注意的是,SyncBarrier在同一个消息队列中可能不止一个,每一个SyncBarrier都与单独的token一一对应。 虽然关于异步消息等操作不能轻易接触到,但其在系统中却也有广泛的应用。例如在ViewRootImpl中当进行ViewTree遍历(measure+layout+draw的过程)时就有用到。在进行ViewTree遍历时会给native层的渲染器(mChoreographer)进行画面渲染,在渲染未结束之前我们希望阻塞住线程,让其不处理同步消息。这时候就通过postSyncBarrier()往消息队列中添加了一个即时的同步栅栏,直到渲染器渲染完成并通过mTraversalRunnable回调到java层之后,才把这个同步栅栏通过removeSyncBarrier给删除掉。 我们在上边讨论next方法的时候曾经说过其内部的nativePollOnce方法是阻塞式的,其内部就是用到了epoll实现的阻塞,这也是Looper实现阻塞式轮询的关键。 nativePollOnce在native层的代码位于android_os_MessageQueue.cpp中,以下就是java层方法与native方法的一一对应关系。 可以看见,nativePollOnce的native层方法只是一个中转站,其调用的是本地消息队列NaviveMessageQueue中的pollOnce方法。当然,这个pollOnce内部最终调用的是Looper.cpp中的pollOnce。其后就是我们epoll的主场了。前边我们在讨论java层的MessageQueue的创建曾经提到过,其构造方法中会调用nativeInit方法创建native的消息队列,而在native消息队列NativeMessageQueue(上边有)构造了native层的Looper。可见在java层消息机制创建时,Android也同时为native准备好了,同样的NativeMessageQueue.cpp与Looper.cpp也是一对一的关系。而我们本小节的主角epoll就是在native层的Looper中创建出来的。以下是epoll创建以及监听事件的关键代码。 在native层的Looper创建时就已经使用epoll监听pipe管道的mWakeReadPipeFd端事件了。通过在篇首储备知识的讲解中,我们可以知道epoll_create用于创建epoll对象,epoll_ctl用于注册监听事件,这两者我们在native层Looper的构造方法中都已看见。而epoll_wait才是epoll实现可阻塞调用的核心方法,它的位置我们不难想象,它一定在java层的消息队列next()调起的方法链的某个地方。没错,就是刚才我们从链上追溯到的mLooper->pollOnce(timeoutMillis)中。native层Looper的pollOnce最终会调用pollInner,在PollInner中我们发现了epoll_wait的身影。 其中timeoutMillis是一路从java层的nativePollOnce传过来的,其用于表示在epoll_ctl监听的事件未发生之前epoll_wait最长可能阻塞的时间。 在epoll_wait开始阻塞之后,那么Android机制又是如何wake唤醒的呢?在java层唤醒是通过位于MessageQueue消息队列中的nativeWake()方法达到的。nativeWake()是native方法。这是因其最终需要把native层的epoll_wait阻塞给唤醒。 在java层,nativeWake主要被三个地方调用: 第一.MessageQueue被插入消息,也就是enqueueMessage方法调用时,这个方法在上边也有说明。 第二.当MessageQueue调用quit试图退出轮询时,因为退出之前需要把消息队列中的消息给取空,或者需要next的nativePollOnce不被阻塞,无论基于那种需要,都需要通过nativeWake唤醒epoll_wait的阻塞。 第三.当我们移除同步栅栏时,如果有必要也需要通过nativeWake取消掉epoll_wait的阻塞。什么是有必要呢,如果当前同步栅栏移除后链首的Message不是另外一个同步栅栏,且消息队列并未quit时,那么就需要调用。 说完nativeWake在java层的调用之后,显然,我们还需要清楚在native层他是怎么唤醒epoll_wait的。在经过层层调用之后,nativeWake会调用到native层Looper的wake方法中。 从上边native层Looper的构造函数中我们可以看见其通过epoll_ctl监听的是mWakeReadPipeFd输出端的EPOLLIN即“可以读取”事件。当我们往管道的另一端mWakeWritePipeFd写入数据时,这个事件就发生了,也就是说epoll_wait一直等待的事件触发了,所以epoll_wait阻塞消除。当然,在阻塞消除之后的pollInnter中需要把pipe中的数据(实质上就是缓冲区的数据给read出来以清空缓冲区)。这就是pollInner调用到的awoken方法。在通过awoken清空缓冲区之后,就进入了下一次mWakeReadPipeFd的EPOLLIN事件的等待。该awoken的调用可在上边关于pollInner的代码段看见。 至此,整个Android消息机制的阻塞原理就说完了,就是通过epoll机制完成的整个next的阻塞。 Message是Android消息机制中具体数据的携带者,开发者或系统可通过使用Handler源源不断的发送Message给轮询器以驱动整个程序的运行。可想而知,当整个程序越复杂,Message如果不在使用之后在复用,程序的Message数量将增长很快,而且频繁new对象将会造成庞大的开销。正基于此,为所有已经处理完毕的废弃消息建立消息池来保存他们,就显得格外重要。 在Looper轮询器的loop方法中,当调用msg.target.dispatchMessage处理完消息之后,这条Message已经处理完毕。接下来就是对这条Message进行回收,即调用msg.recycleUnchecked()把它加入到程序的消息池中以待下次复用。 这个消息池就是Message中的静态成员sPool,sPool是一个静态的单链表,这表示sPool不光只为某个线程服务,在进程中,所有地方都可以接触到这个消息池。从下边可以看到消息池的最大容量为50,初始大小即sPoolSize为0。 所有已经处理完成的消息都会通过recycleUnchecked加入到消息池,这是因为通过Handler投递的Message都会进入loop()循环中,而循环末尾必然调用recycleUnchecked。而从消息池获取消息则是通过Message.obtain()。在obtain()方法中会首先尝试从消息池中获取一条消息,如果消息池为空,则会自动创建一个新的消息给开发者使用,所以这里不用担心obtain()方法会返回空的问题,由于obtain()有多个重载方法,但原理相同,这里我们仅贴其一。 曾经,我们也许通过Handler发送的消息都是通过new Message()的方式得到的,但这是一种不良行为,虽然最后这条消息最终也会调用recycleUnchecked方法,但通过这种方式频繁创建的话,由于sPool消息池最大容量的限制,这条消息将被finalize掉,造成了内存的抖动。所以最优的使用方式可通过Message.Obtain()或mHandler.obtainMessage()(最终也是调用前者)来获取消息。 在多数场景中,开发者都只是在进程内部使用Handler来往不同线程发送Message。这也就让大家忽视了Handler的另外一个神奇的能力即跨进程通信。也就是说Android消息机制不局限于线程间使用,还可跨进程传递Message。 每个Handler其实内部都有提供有一个信使,这个信使就是下边的MessengerImpl。不难发现MessengerImpl依赖于Binder机制,正因如此,就使得Handler具备了进程通信的能力。但是同时我们也可以发现这个信使是私有的,且getIMessager也因修饰符不对外开放。所以如何获得Handler中的信使也自然而然就成了我们用Handler实现跨进程的关键。 为了获得Handler进程通信的能力,这就需要使用到Messenger类,Messenger本质上是对Handler的包装,同时它也隐藏了binder方面的逻辑。但这不妨碍我们使用它来获取超能力。那该怎么使用Messenger呢?其实就在于Messenger的两个构造方法。 参数为Handler的构造方法就是提供Handler的进程,也即Message目标进程。而参数为IBinder的构造方法,则是Message发送进程获取目标进程Handler的主要方式。这两个构造方法分别对两个进程涉及到的Handler/Binder进行了包装。 举个例子!!! 假设我们构建一个Service,其单独属于某个进程,那么我们可以将这个Service中的Handler通过以下方式提供给其他进程。通过使用Messenger包装Handler,当然其实质上是通过调用Handler.getIMessenger()来获取到前边提到过的具有跨进程能力的MessengerImpl。它本质上是一个Binder,所以我们可以通过messenger.getBinder()把这个信使暴露出去。至于为什么Messenger可以获取到Handler内部的信差,那当然是因为Messenger与Handler位于同一包目录下啦。 而其他进程想要获取MessengerService的Handler并往其发送消息。那么这个进程应该类似于以下调用。其也是通过Messenger的构造完成了信使的获取,但这个构造方法内部只不过对目标进程Handler内部的MessengerImpl做了asInterface()处理。实质上与一般的bindService方式没有啥区别。 以上例子的源码在文末有贴出。需要注意的是必须使用Bundle传递常规类型的数据,否则就会报以下错误: 我们知道,Android应用程序是基于消息驱动的,当你从Launcher桌面点开某个应用,而这个应用的入口就是ActivityThread,更确切的说,是ActivityThread中的main函数。而在main函数所在的线程当然就是这个程序的主线程,在这个主线程中,我们就发现了Android消息机制的身影。 可以看到,与一般线程使用Looper.prepare()创建Looper不同的是,主线程的使用过prepareMainLooper创建了一个sMainLooper。而且在后边调用了Looper.loop()使这个main线程一直处于消息轮询的过程中,如果轮询退出,则意味着主线程即进程挂掉了。这就充分说明,一个android程序的运行,是基于Looper.loop()进行不断消息轮询来驱动的。 HandlerThread是对Android消息机制的封装后的Thread。你就需要获取到HandlerThread的Looper对象,就可以从任意地方往这个线程里边发送Message/Runnable。关于HandlerThread如何使用等细节,读者可以阅读我的这篇文章:【多线程】用HandlerThread管理线程和控制异常。 在介绍完Android的消息机制后,我们不难发现,这套机制的排序是基于msg.when也就是时间来排序的。但某些场景下,可能不能完全满足需要。比方说,根据业务需要,把消息类型分为了控制类型和普通业务类型。然而我们希望控制类型的消息具有优先处理权,也就是说无论在消息队列中还有多少消息,只要这个控制类型消息一投送,就希望消息机制能够第一时间被处理,那么这个情况下,Android本身的Handler消息机制就不适用了。 所以我们构建了一套基于优先级的消息机制,这套消息机制除了使用Handler的思想之外,还具备了以下不同于Android消息机制的特点。 1.所有的消息都可分为不同的level优先级,高优先级的消息被优先轮询。 2.轮询器也基于阻塞式轮询,但其废弃了epoll式的阻塞,而是使用wait/notify方式。 3.SyncBarrier可阻塞>=某个优先级level的所有的同步消息,直到主动移除它。 4.兼容于按时间排序的方式。 以下是源码附送: 基于优先级的消息机制 Handler跨进程通信DemoSyncBarrier
/**
* @hide
*/
public Handler(Callback callback, boolean async) {
...
mAsynchronous = async;
}
/**
* @hide
*/
public Handler(Looper looper, Callback callback, boolean async) {
...
mAsynchronous = async;
}
//截自消息队列的next()方法
...
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//如果发现当前的消息是SyncBarrier消息(通过target==null判断),则取出消息队列中的异步消息进行处理,而阻塞掉大部分的同步消息
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
...
/**
* @hide
*/
public int postSyncBarrier() {
return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis());
}
/**
* @hide
*/
public void removeSyncBarrier(int token) {
mQueue.removeSyncBarrier(token);
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
...
}
}
epoll使用
static JNINativeMethod gMessageQueueMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit },
{ "nativeDestroy", "(J)V", (void*)android_os_MessageQueue_nativeDestroy },
{ "nativePollOnce", "(JI)V", (void*)android_os_MessageQueue_nativePollOnce },
{ "nativeWake", "(J)V", (void*)android_os_MessageQueue_nativeWake },
{ "nativeIsIdling", "(J)Z", (void*)android_os_MessageQueue_nativeIsIdling }
};
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jclass clazz,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, timeoutMillis);
}
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
return nativeMessageQueue->wake();
}
void NativeMessageQueue::pollOnce(JNIEnv* env, int timeoutMillis) {
mInCallback = true;
mLooper->pollOnce(timeoutMillis);
mInCallback = false;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
int wakeFds[2];
//构造epoll监听对象pipe
int result = pipe(wakeFds);
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
...
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
...
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeReadPipeFd;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
...
}
int Looper::pollInner(int timeoutMillis) {
...
// We are about to idle.
mIdling = true;
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// No longer idling.
mIdling = false;
...
//此处就是事件发生之后对Fd与事件类型判断后对相应缓冲区进行清空,方便epoll_wait重新进入下次等待
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeReadPipeFd) {
if (epollEvents & EPOLLIN) {
awoken();
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
}
} else {
...
}
}
private native static void nativeWake(long ptr);
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif
ssize_t nWrite;
do {
nWrite = write(mWakeWritePipeFd, "W", 1);
} while (nWrite == -1 && errno == EINTR);
if (nWrite != 1) {
if (errno != EAGAIN) {
ALOGW("Could not write wake signal, errno=%d", errno);
}
}
}
void Looper::awoken() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ awoken", this);
#endif
char buffer[16];
ssize_t nRead;
do {
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
}
消息池
public static void loop() {
...
for (;;) {
Message msg = queue.next(); // might block
...
//target是保存在消息中的Handler引用,此处的dispatchMessage所在线程为loop方法调用所在线程
msg.target.dispatchMessage(msg);
...
//消息在发送完之后自动回收到保存在Message中的pool中
msg.recycleUnchecked();
}
}
//进程唯一性的锁
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
//取出来的消息,要去掉链表属性next
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
扩展说明
Handler如何跨进程通信
public class Handler {
...
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}
...
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
...
}
public final class Messenger implements Parcelable {
...
private final IMessenger mTarget;
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
...
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
}
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
public MessengerService() {
}
@Override
public IBinder onBind(Intent intent) {
Messenger messenger = new Messenger(new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.i(TAG, "handleMessage: "+msg.getData().get("data"));
}
});
return messenger.getBinder();
}
}
bindService(new Intent(this, MessengerService.class), new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Messenger messenger = new Messenger(service);
for (int i = 0; i < 10; i++) {
Message message = Message.obtain();
try {
message.getData().putString("data","我是来自另外进程的消息"+i);
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, Context.BIND_AUTO_CREATE);
java.lang.RuntimeException: Can't marshal non-Parcelable objects across processes.
消息机制的应用
ActivityThread
public static void main(String[] args) {
//初始化工作
...
Looper.prepareMainLooper();
//在new ActivityThread时会创建Handler,所以通过thread.getHandler拿到的当然是主线程的Looper
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
HandlerThread
按优先级处理的消息机制