2.3.2 提取消息

当一切准备就绪后,Java层的消息循环处理,也就是Looper会在一个循环中提取并处理消息。消息的提取就是调用MessageQueue的next函数。当消息队列为空时,next函数就会阻塞。MessageQueue同时支持Java层和Native层的事件,其next函数的实现代码如下:

[—>MessagQueue.java:next]


final Message next(){

int pendingIdleHandlerCount=-1;

int nextPollTimeoutMillis=0;

for(;){

……

//mPtr保存了NativeMessageQueue的指针,调用nativePollOnce进行等待nativePollOnce(mPtr, nextPollTimeoutMillis);

synchronized(this){

final long now=SystemClock.uptimeMillis();

//mMessages用来存储消息,这里从其中取一个消息进行处理final Message msg=mMessages;

if(msg!=null){

final long when=msg.when;

if(now>=when){

mBlocked=false;

mMessages=msg.next;

msg.next=null;

msg.markInUse();

return msg;//返回一个Message给Looper进行派发和处理

}else{

nextPollTimeoutMillis=(int)Math.min(when-now,

Integer.MAX_VALUE);

}

}else{

nextPollTimeoutMillis=-1;

}

……

/*

处理注册的IdleHandler,当MessageQueue中没有Message时,

Looper会调用IdleHandler做一些工作,例如做垃圾回收等。请读者务必记住此处的处置逻辑,以后分析Activity启动时会用到这里的知识

*/

……

pendingIdleHandlerCount=0;

nextPollTimeoutMillis=0;

}

}


看到这里,可能有人会觉得这个MessageQueue很简单,不就是从以前在Java层的wait变成现在Native层的wait了吗?但是事情本质比表象要复杂得多,来思考下面的情况:

nativePollOnce返回后,next函数将从mMessages中提取一个消息。也就是说,要让nativePollOnce返回,至少要添加一个消息到消息队列,否则nativePollOnce不过是做了一次无用功罢了。

如果nativePollOnce在Native层等待,就表明Native层也可以投递Message(消息,为了适应业界的习惯本书沿用英文,必要时才用中文,其他词同此),但是从Message类的实现代码上看,该类和Native层没有建立任何关系(即Native层不太可能去构造Java层的Message对象并把它插入到Java层的Message队列中)。那么nativePollOnce在等待什么呢?

对于上面的问题,相信有些读者心中已有了答案:nativePollOnce除了等待Java层来的Message,还在Native层做了大量的工作。

下面我们来分析Java层投递Message并触发nativePollOnce工作的正常流程。

1.在Java层投递Message

MessageQueue的enqueueMessage函数完成将一个Message投递到MessageQueue中的工作,其代码如下:

[—>MesssageQueue.java:enqueueMessage]


final boolean enqueueMessage(Message msg, long when){

……

final boolean needWake;

synchronized(this){

if(mQuiting){

return false;

}else if(msg.target==null){

mQuiting=true;

}

msg.when=when;

Message p=mMessages;

if(p==null||when==0||when<p.when){

/*

如果p为空,表明消息队列中没有消息,那么msg将是第一个消息,needWake需要根据mBlocked的情况考虑是否触发

*/

msg.next=p;

mMessages=msg;

needWake=mBlocked;

}else{

//如果p不为空,表明消息队列中还有剩余消息,需要将新的msg加到消息队列尾部

Message prev=null;

while(p!=null&&p.when<=when){

prev=p;

p=p.next;

}

msg.next=prev.next;

prev.next=msg;

//因为消息队列之前还有剩余消息,所以这里不用调用nativeWakeup needWake=false;

}

}

if(needWake){

//调用nativeWake,以触发nativePollOnce函数结束等待nativeWake(mPtr);

}

return true;

}


上面的代码比较简单,主要功能是:

将Message按执行时间排序,并加入消息队列。

根据情况调用nativeWake函数,以触发nativePollOnce函数,结束等待。

建议 虽然代码简单,但是对于那些不熟悉多线程的读者,还是要细细品味一下mBlocked值的作用的。我们常说细节体现美,代码也一样,这个小小的mBlocked正是如此。

2.nativeWake函数分析

nativeWake函数的代码如下所示:

[—>android_os_MessageQueue.cpp:android_os_MessageQueue_nativeWake]


static void android_os_MessageQueue_nativeWake(JNIEnv*env, jobject obj,

jint ptr)

{

NativeMessageQueue*nativeMessageQueue=//取出NativeMessageQueue对象

reinterpret_cast<NativeMessageQueue*>(ptr);

return nativeMessageQueue->wake();//调用它的wake函数

}

void NativeMessageQueue:wake(){

mLooper->wake();//层层调用,现在转到mLooper的wake函数

}


Native层Looper的wake函数代码如下:

[—>Looper.cpp:wake]


void Looper:wake(){

ssize_t nWrite;

do{

//向管道的写端写入一个字符

nWrite=write(mWakeWritePipeFd,"W",1);

}while(nWrite==-1&&errno==EINTR);

if(nWrite!=1){

if(errno!=EAGAIN){

LOGW("Could not write wake signal, errno=%d",errno);

}

}

}


wake函数则更为简单,仅仅向管道的写端写入一个字符“W”,这样管道的读端就会因为有数据可读而从等待状态中醒来。