• Looper中的睡眠等待与唤醒机制


    Looper中的睡眠等待与唤醒机制

    C++类Looper中的睡眠和唤醒机制是通过pollOnce和wake函数提供的,它们又是利用操作系统(Linux内核)的epoll机制来完成的。当被监控的文件(通过epoll_ctl的EPOLL_CTL_ADD添加进去)可I/O时,epoll_wait调用会从睡眠中醒来,这时,可以检查是哪个(或哪些)文件描述符对应的文件可以进行I/O读写了,从而做出进一步处理。使用者利用它们就可以拥有睡眠等待和唤醒机制。下面详述。

    在Looper的构造函数中,会创建一个管道(下面的行73),然后调用epoll_create获取一个epoll的实例的描述符(行88),最后将管道读端描述符作为一个事件报告项添加给epoll(行95)。这样,当管道读端有数据可读时,将会得到报告。Looper的构造函数如下(见文件Looper.cpp):

     

    Looper的pollOnce函数将最终调用到其pollInner函数。在后者里面,将调用epoll_wait睡眠等待其监控的文件描述符是否有可I/O事件的到来,若有(哪怕只有一个),epoll_wait将会醒来,然后可检查是哪个文件描述符上的可I/O事件。pollInner函数中的相关代码如下(见文件Looper.cpp):

    可见,在线程循环中调用了Looper的pollOnce函数,将导致睡眠等待在上面的行218处的epoll_wait上。当向消息队列发送消息并进行唤醒时,行218将被唤醒,因此从pollOnce函数中返回,可以从消息队列中取出消息进行处理。

    Looper的wake函数用于向管道中写入字符(下面的行367),以唤醒pollOnce:

    下面来看一下Java层的MessageQueue如何利用这种机制。

    前面提到在android.os.MessageQueue的next函数中取出下一个消息时,会调用到native层实现的函数nativePollOnce时,实际调用到了如下native实现(见文件android_os_MessageQueue.cpp):

     

    上面行157的pollOnce函数代码是(见文件android_os_MessageQueue.cpp):

    这样,它们就通过Looper的pollOnce实现了在Looper中的管道上的读端上的睡眠等待。

    当android.os.MessageQueue的enqueueMessage函数往队列上添加了一个新消息或removeSyncBarrier移除了同步屏障后,可能需要调用nativeWake唤醒,其native实现为:(见文件android_os_MessageQueue.cpp):

      上面的行162调用的又是下面的函数,代码如下(见文件android_os_MessageQueue.cpp):

      这样,Looper将向管道写端写入字符,唤醒其在管道读端上的睡眠等待。

    因此,通过借助于Looper的wake和pollOnce函数,可以让别的消息队列(如Java层的消息队列)拥有睡眠唤醒机制:没有消息时pollOnce调用者将睡眠等待,有消息时让wake函数去唤醒睡眠等待。

    本文节选自《深入剖析Android系统》一书

    杨长刚著

    电子工业出版社出版

  • 相关阅读:
    转:CSS设置HTML元素的高度与宽度的各种情况总结
    Java、mysql、html、css、js 注释&大小写
    Dom4j与sax 简单对比
    转:Java properties | FileNotFoundException: properties (系统找不到指定的文件。)
    转:SAX解析的characters方法被多次调用
    转:HashMap实现原理分析(面试问题:两个hashcode相同 的对象怎么存入hashmap的)
    转:Scanner中nextLine()方法和next()方法的区别
    转:JDBC中关于PreparedStatement.setObject的一些细节说明
    转:Eclipse 各种小图标的含义
    转:Mysql float类型where 语句判断相等问题
  • 原文地址:https://www.cnblogs.com/broadview/p/2881284.html
Copyright © 2020-2023  润新知