您的位置:首页 > 移动开发 > Android开发

[M0]Android Native层Looper详解

2015-07-19 11:24 701 查看
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。/article/8794749.html

前言

我们知道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接口说明

FunctionDesription
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 类图如下:



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<LooperCallback>& callback, void* data);


Create Looper

Looper提供两种方式创建Looper:

直接调用Looper 构造函数:

Looper(bool allowNonCallbacks);


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

static sp<Looper> 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<MessageHandler>& handler, const Message& message);
void sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
const Message& message);
void sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
const Message& message);


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

首先根据uptime在mMessageEnvelopes遍历,找到合适的位置,并将message 封装成MessageEnvlope,插入找到的位置上。

然后决定是否要唤醒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的实现就介绍到这里了。如有不清楚的地方,欢迎留言。

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。/article/8794749.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: