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”,这样管道的读端就会因为有数据可读而从等待状态中醒来。