读书频道 > 网站 > 网页设计 > 深入理解Android:卷III
2.3.2提取消息
15-08-21    下载编辑
收藏    我要投稿   

本文所属图书 > 深入理解Android:卷III

本书在逻辑上分为4个部分Part 01(第1-2章):这是本书的基础部分,首先介绍了Android源码环境的搭建、编译和调试;然后讲解了Android进程间通信与任务调度的工具Binder与MessageQueue 这两项基础工作是深入研究立即去当当网订购
当一切准备就绪后,Java层的消息循环处理,也就是Looper会在一个循环中提取并处理消息。消息的提取就是调用MessageQueue的next()方法。当消息队列为空时,next就会阻塞。MessageQueue同时支持Java层和Native层的事件,那么其next()方法该怎么实现呢?具体代码如下:
 
[MessagQueue.java-->MessageQueue.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做一些工作,例如做垃圾回收等  */
            ......
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }
}

 

 
看到这里,可能会有人觉得这个MessageQueue很简单,不就是从以前在Java层的wait变成现在Native层的wait了吗?但是事情本质比表象要复杂得多。请思考下面的情况:
 
在nativePollOnce()返回后,next()方法将从mMessages中提取一个消息。也就是说,要让nativePollOnce()返回,至少要添加一个消息到消息队列,否则nativePollOnce()不过是做了一次无用功罢了。
 
如果nativePollOnce()将在Native层等待,就表明Native层也可以投递Message,但是从Message类的实现代码上看,该类和Native层没有建立任何关系。那么nativePollOnce()在等待什么呢?
 
对于上面的问题,相信有些读者心中已有了答案:nativePollOnce()不仅在等待Java层来的Message,实际上在Native层还做了大量工作。
 
下面我们来分析Java层投递Message并触发nativePollOnce工作的正常流程。
 
1. 在Java层投递Message
 
MessageQueue的enqueueMessage函数完成将一个Message投递到MessageQueue中的工作,其代码如下:
 
[MesssageQueue.java-->MessageQueue.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函数
}
[android_os_MessageQueue.cpp-->NativeMessageQueue::wake()]
void NativeMessageQueue::wake() {
    mLooper->wake(); // 层层调用,现在转到mLooper的wake函数
}
Native Looper的wake函数代码如下:
[Looper.cpp-->Looper::wake()]
void Looper::wake() {
    ssize_t nWrite;
    do {
        // 向管道的写端写入一个字符
        nWrite = write(mWakeWritePipeFd, "W", 1);
    } while (nWrite == -1 && errno == EINTR);
}

 

Wake()函数则更为简单,仅仅向管道的写端写入一个字符“W”,这样管道的读端就会因为有数据可读而从等待状态中醒来。
点击复制链接 与好友分享!回本站首页
分享到: 更多
您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:1.3 功能
下一篇:1.5 小结
相关文章
图文推荐
JavaScript网页动画设
1.9 响应式
1.8 登陆页式
1.7 主题式
排行
热门
文章
下载
读书

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训
版权所有: 红黑联盟--致力于做最好的IT技术学习网站