[M0]Android Native层Looper详解

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://blog.csdn.net/chwan_gogogo/article/details/46953549

前言

我们知道Java 层的Looper 的消息队列在没有消息处理的时候,会wait在MessageQueue.next() 函数里,对于MessageQueue.next() 函数是如何实现的wait,却是一知半解。而且Android Framework部分有很多在Native层使用Looper 监听文件描述符的用法,比如InputDispatcher等,了解Android Native 层Looper的实现,可以对整个Android系统的消息循环机制有更深入的理解。

代码版本
http://androidxref.com/6.0.1_r10/xref/system/core/include/utils/Looper.h
http://androidxref.com/6.0.1_r10/xref/system/core/libutils/Looper.cpp

Looper接口说明

Function Desription
Looper(bool allowNonCallbacks); Looper 构造函数
static sp< Looper> prepare(int opts); 如果该线程没有绑定Looper,才创建Looper,否则直接返回。
int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData); 轮询,等待事件发生
void wake(); 唤醒Looper
void sendMessage(const sp< MessageHandler>& handler, const Message& message); 发送消息
int addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data); 添加要监听的文件描述符fd

Class Diagram

Looper 类图如下:
[M0]Android Native层Looper详解_第1张图片

sendMessage()时,需要指定MessageHandler, 根据MessageMessageHandler创建MessageEnvelope 。然后将MessageEnvelope 添加到Looper的消息队列 mMessageEnvelopes 中。
Native Looper除了处理Message之外,还可以监听指定的文件描述符
- 通过addFd() 添加要监听的fd到epoll的监听队列中,并将传进来的fd,ident,callback,data 封装成Request 对象,然后加入到Looper 的mRequests 中。
- 当该fd有事件发生时,epoll_wait()会返回epoll event,然后从mRequests中找到对应的request对象,并加上返回的epoll event 类型(EPOLLIN、EPOLLOUT…)封装成Response对象,加入到mResponses 中。
- 然后在需要处理Responses的时候,从mResponses遍历取出Response进行处理。

addFd() 的具体使用介绍请参考 Android Native Looper机制 - 监听文件描述符

int addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data);
int addFd(int fd, int ident, int events, const sp& callback, void* data); 

Create Looper

Looper提供两种方式创建Looper:
直接调用Looper 构造函数:

Looper(bool allowNonCallbacks);

调用Looper的静态函数 prepare() :如果线程已经有对应的Looper,则直接返回,否则才会创建新的Looper。

static sp prepare(int opts);

Looper的构造函数里主要做两件事情:
- 调用eventfd(0, EFD_NONBLOCK)返回mWakeEventFd,用于唤醒epoll_wait()
- 调用rebuildEpollLocked() 创建epoll 文件描述符,并将mWakeEventFd加入到epoll监听队列中

mEpollFd = epoll_create(EPOLL_SIZE_HINT);
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd;
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);

关于mWakeEventFd

关于eventfd也是linux新增的一个API,用于线程之间的通信。之前版本中是通过pipe,得到read&write fd:来实现的wakeup looper功能。
当需要唤醒Looper时,调用wake(), 会往mWakeEventFd中write 1

void Looper::wake() {
    ...
    uint64_t inc = 1;
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));

epoll_wait() 则会返回。然后调用awoken() ,

void Looper::awoken() {
    ...
    uint64_t counter;
    TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));

Send Message

发送消息是指将消息插入到消息队列 mMessageEnvelopes。mMessageEnvelopes 里面的是根据时间顺序排列存放MessageEnvlope:下标越小,越早被处理。

发送消息的函数有如下三个,但最终都是调用sendMessageAtTime() 来实现的。

void sendMessage(const sp& handler, const Message& message);
void sendMessageDelayed(nsecs_t uptimeDelay, const sp& handler,
            const Message& message);
void sendMessageAtTime(nsecs_t uptime, const sp& handler,
            const Message& message);

来看一下sendMessageAtTime()函数的具体实现:

  1. 首先根据uptime在mMessageEnvelopes遍历,找到合适的位置,并将message 封装成MessageEnvlope,插入找到的位置上。
  2. 然后决定是否要唤醒Looper:
    • 如果Looper此时正在派发message,则不需要wakeup Looper。因为这一次looper处理完消息之后,会重新估算下一次epoll_wait() 的wakeup时间。
    • 如果是插在消息队列的头部,则需要立即wakeup Looper。

Poll Looper

要让Looper运行起来才能处理消息。
Looper提供了接口:pollOnce()
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);

如果Looper中没有任何要处理的event/message,则会阻塞在epoll_wait() 等待事件到来。
调用流程:pollOnce() ->pollInner()->epoll_wait()

    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

epoll_wait()其有三种情况会返回,返回值eventCount为上来的epoll event数量。
- 出现错误返回, eventCount < 0;
- timeout返回,eventCount = 0,表明监听的文件描述符中都没有事件发生,将直接进行native message的处理;
- 监听的文件描述符中有事件发生导致的返回,eventCount > 0; 有eventCount 数量的epoll event 上来。

Dispatch Message

Message/Event 都是在pollOnce() 和 pollInner() 中派发处理的。

pollOnce()

  • 主要是 先处理mResponses中 还没有被处理的事件:加入epoll监听列表的fd,但是没有指定callback,但是可以通过返回事件的ident来require caller进行处理的。
  • 然后调用pollInner(), pollInner() 调用 epoll_wait() 阻塞等待。这里会处理消息队列 mMessageEnvelopes 的Message,和 mResponses中ident 为POLL_CALLBACK的事件。
  • pollInner() 中只处理了mResponses中ident 为POLL_CALLBACK的事件,其他ident >= 0的事件,则在这时处理,处理完返回ident,以便caller 知道是否需要继续处理。

pollInner():

  • 调整timeout:Adjust the timeout based on when the next message is due.

    • mNextMessageUptime 是 消息队列 mMessageEnvelopes 中最近一个即将要被处理的message的时间点。
    • 所以需要根据mNextMessageUptime 与 调用者传下来的timeoutMillis 比较计算出一个最小的timeout,这将决定epoll_wait() 可能会阻塞多久才会返回。
  • epoll_wait()

  • 处理epoll_wait() 返回的epoll events.
    判断epoll event 是哪个fd上发生的事件
    • 如果是mWakeEventFd,则执行awoken(). awoken() 只是将数据read出来,然后继续往下处理了。其目的也就是使epoll_wait() 从阻塞中返回。
    • 如果是通过Looper.addFd() 接口加入到epoll监听队列的fd,并不是立马处理,而是先push到mResponses,后面再处理。
  • 处理消息队列 mMessageEnvelopes 中的Message.
    • 如果还没有到处理时间,就更新一下mNextMessageUptime
  • 处理刚才放入mResponses中的 事件.
    • 只处理ident 为POLL_CALLBACK的事件。其他事件在pollOnce中处理

小结

主要针对三部分的事件进行派发处理:
- native层通过 sendMessage() 存放在mMessageEnvelopes中的MessageEnvelope。
- 通过 addFd() 加入epoll监听队列的fd,并且其对应的callback不为null的。
- 剩下的就是加入epoll监听列表的fd,但是没有指定callback,但是可以通过返回事件的ident来require caller进行处理的。
他们被处理的顺序如同以上描述顺序。

总结

关于Android Native 层的Looper的实现就介绍到这里了。如有不清楚的地方,欢迎留言。

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://blog.csdn.net/chwan_gogogo/article/details/46953549

你可能感兴趣的:(Android,Common)