上一章关注的时Looper的应用,本章则聚焦于其实现。诚然 AOSP 是开源的,且它的文档相较其他个人开源项目来说已经够有诚意了,但还是不如API文档那般事无巨细的说明。所以使用Android提供的轮子前,还是要扒开看看它内部的。
sp<Looper> Looper::prepare(int opts) {
//code 1
bool allowNonCallbacks = opts & PREPARE_ALLOW_NON_CALLBACKS;
//code 2
sp<Looper> looper = Looper::getForThread();
if (looper == nullptr) {
looper = sp<Looper>::make(allowNonCallbacks);
//code 3
Looper::setForThread(looper);
}
}
在调用Looper::prepare时传入标志 PREPARE_ALLOW_NON_CALLBACKS,意味着在跟踪fd的时候是可以不传入供回调的方法或者类实现的。当此处的fd产生被跟踪事件时,poolAll会退出内部的循环,并且返回调用 addFd时传入的indent。下面是简单应用的例子
main
//开始监听给定fd
ret = myLooper->addFd(myBitTube->getFd(), NULL_CALLBACK_TO_HANDLE_ID/*indent*/,
android::Looper::EVENT_INPUT,
nullptr,
nullptr);
//fd来事件后的处理
std::thread msgHandler( [looper = myLooper, &msgHandler, &myBitTube]{
for (;;) {
int pollResult = looper->pollAll(-1 /* timeout */);
if (pollResult == NULL_CALLBACK_TO_HANDLE_ID) {
...
}
}
});
在一个线程中第一次调用Looper::getForThread的时候,其必然是nullptr的,所以这里的流程则是构造Looper,如下则是对应的实现代码
Looper::Looper(bool allowNonCallbacks)
: mAllowNonCallbacks(allowNonCallbacks),
{
...
rebuildEpollLocked();
}
rebuildEpollLocked 则由如下步骤组成
a)更新epoll fd,因为rebuildEpollLocked在Looper请求重建内部epoll句柄的时候会被再次调用。
b)创建一个 epoll_event ,并将其加入 epoll 的监听队列中,其依赖的fd如下,作用则是用于后面即将介绍的
//system\core\libutils\include\utils\Looper.h
class Looper : public RefBase {
private:
android::base::unique_fd mWakeEventFd; // immutable
};
创建的 epoll_event 如下
epoll_event wakeEvent = createEpollEvent(EPOLLIN, WAKE_EVENT_FD_SEQ);
return {.events = events, .data = {.u64 = seq}};
加入 epoll监听队列
int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &wakeEvent);
c)因为rebuildEpollLocked在Looper请求重建内部epoll句柄的时候会被再次调用,但是重建后不能丢失原来的请求,所以需要重新将request中包含的句柄添加到新的epoll fd中去。至于request的添加及处理下面会进行详细的讲解。
for (const auto& [seq, request] : mRequests) {
epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
}
就是对 pthread_setspecific/pthread_getspecific 家族的封装,目的则是实现资源在线程中的单例性。
Looper是用来监听事物的例如文件句柄和Message,但是对于监听哪些事物则通过暴露接口addFd供用户添加,下面是它所有的定义
//1
int addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data);
//2
int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data);
第一种传入的回调可以是函数指针,但是实现中还是会将函数指针封装成类 SimpleLooperCallback,再调用第二种。所以下面只要分析第二种即可,下面是 SimpleLooperCallback 的定义
//system\core\libutils\include\utils\Looper.h
class SimpleLooperCallback : public LooperCallback {
protected:
virtual ~SimpleLooperCallback();
public:
SimpleLooperCallback(Looper_callbackFunc callback);
virtual int handleEvent(int fd, int events, void* data);
private:
Looper_callbackFunc mCallback;
};
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
//code 1
if (!callback.get()) {
if (! mAllowNonCallbacks) {
return -1;
}
} else {
ident = POLL_CALLBACK;
}
//code 2
if (mNextRequestSeq == WAKE_EVENT_FD_SEQ) mNextRequestSeq++;
const SequenceNumber seq = mNextRequestSeq++;
//code 3
Request request;
request.fd = fd;
request.ident = ident;
request.events = events;
request.callback = callback;
request.data = data;
epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
//code 4
auto seq_it = mSequenceNumberByFd.find(fd);
if (seq_it == mSequenceNumberByFd.end()) {//code 4-1
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem);
mRequests.emplace(seq, request);
mSequenceNumberByFd.emplace(fd, seq);
} else {//code 4-2
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_MOD, fd, &eventItem);
const SequenceNumber oldSeq = seq_it->second;
mRequests.erase(oldSeq);
mRequests.emplace(seq, request);
seq_it->second = seq;
}
return 1;
}
如果创建Looper的时候没有指定标志PREPARE_ALLOW_NON_CALLBACKS,那么在添加监听fd的时候是不允许回调类或者方法为空的。所以对 mAllowNonCallbacks 进行了判断。反之提供了回调类或方法,这意味着本次的请求为 POLL_CALLBACK, 在后续线程调用pollAll过程中是不会退出并返回ident值的,而是直接调用给定的回调类或方法去处理。
用来统计requestion的数量,当然也作为每一个requestion的唯一标识符。
在Looper中,使用Request来封装每一个请求,其内部记录了被轮询的文件句柄(fd)、唯一标识符(indent)、epoll监听的事件(event)以及该fd产生event的处理方法(callback)等。然后根据传入的fd及seq创建一个epoll_event,待epoll_wait返回时使用。
mRequests则是一个键值对,可以通过seq查找到对应的Request。
//system\core\libutils\include\utils\Looper.h
std::unordered_map<SequenceNumber, Request> mRequests; // guarded by mLock
std::unordered_map<int /*fd*/, SequenceNumber> mSequenceNumberByFd; // guarded by mLock
从两个map的申明来看,可以看到如下映射关系
fd --> seq --> Request
在添加一个Request的时候分为两种情况
a) fd为第一次被监听的,那么就直接加到epoll的监听队列去。对应代码为code 4-1。
b) fd已经被监听过了,那么就要修改下fd对应的event了,因为既然是第二次监听,那么回调方法和数据也是不同的。并且要重新建立下映射关系(fd–>seq–>Request),对应代码4-2。
至此,通过addFd就构建了一个Request,以键值对的形式存储到 mRequests中。并且将传入的fd添加到了epoll的监听队列中去了。
在上一章节也提到过,事件的处理线程实际上就是调用 Looper::pollAll 接口的,上一章的测试程序中的处理线程就是 msgHandler,pollAll的调用流程比较简单,下面不画流程图了
//Looper.cpp (platform\system\core\libutils\include\utils
pollAll
if (timeoutMillis <= 0) {
int result;
do {
result = pollOnce(timeoutMillis, outFd, outEvents, outData);
} while (result == POLL_CALLBACK);
return result;
}
我们默认入参-1,也就是不超时返回,只有pollOnce返回非 POLL_CALLBACK 的时候才退出,这种方式的应用上面也已经提到了。下面就来看看 pollOnce
return pollOnce(timeoutMillis, nullptr, nullptr, nullptr);
for (;;) {
//code 1
while (mResponseIndex < mResponses.size()) {
}
//code 2
if (result != 0) {
if (outFd != nullptr) *outFd = 0;
if (outEvents != nullptr) *outEvents = 0;
if (outData != nullptr) *outData = nullptr;
return result;
}
//code 3
result = pollInner(timeoutMillis)
}
code 1 则是用于处理支持无回调的Looper实例的,即返回indent给到处理线程,至于如何处理该ident则由用户决定了。
pollInner 中是不会返回非零值的,所以此处虽然存在两个循环,但其功能也是符合其命名的,即每次结束都会返回。下面是 pollInner 的实现代码,我们只关注Request的处理代码,
pollInner
//code 1
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
//code 2
for (int i = 0; i < eventCount; i++) {
const auto& request_it = mRequests.find(seq);
const auto& request = request_it->second;
mResponses.push({.seq = seq, .events = events, .request = request});
}
...
//code 3
for (size_t i = 0; i < mResponses.size(); i++) {
Response& response = mResponses.editItemAt(i);
if (response.request.ident == POLL_CALLBACK) {
//code 3-1
int callbackResult = response.request.callback->handleEvent(fd, events, data);
if (callbackResult == 0)
removeSequenceNumberLocked(response.seq);
result = POLL_CALLBACK;
}
}
//code 4
return result;
当被加入epoll中的fd被捕捉到对应的监听事件后,epoll_wait则会返回并且会将 epoll_event 存放到数组 eventItems中,下面是 epoll_event的定义
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
前面addFd的时候会把 epoll_event.data.u64设置成seq,所以可以通过返回的 epoll_event 来找到对应的 Request从而进行处理。
在Request返回后,说明其需要被处理了,那么此时就会创建 Response,下面是其定义
//system\core\libutils\include\utils\Looper.h
struct Response {
SequenceNumber seq;
int events;
Request request;
};
记录了seq、events及request,其中request是包含callback的。如果epoll_wait返回了多个request的话,那么也会产生多个Request并存于 mResponses。
满足 ident 为POLL_CALLBACK的request中的 handleEvent 则会被回调,对应上一章中类 FlagstaffLooperCallback::handleEvent。
//test\flagstafftest\looper_test\FlagstaffLooperTest.cpp
class FlagstaffLooperCallback : public android::LooperCallback{
public:
int handleEvent(int fd, int events, void* data) override {
BitTube::recvObjects<MyData>(bitTube, &myData, 1);
LOG(INFO)<<__func__<<":[myData.mCount]"<<myData.mCount<<"[events]"<<events<<"[fd]"<<fd;
return 1;
}
};
对于 handleEvent 中返回零值那么就意味了被监听的fd只是一次性的,在 removeSequenceNumberLocked 中则会调用epoll_ctrl来剔除其对fd的监听。
removeSequenceNumberLocked
mRequests.erase(request_it);
mSequenceNumberByFd.erase(fd);
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_DEL, fd, nullptr);
返回值只有下面两种情况
a)POLL_CALLBACK,且pollAll入参为-1,那么pollAll是不会返回的,直接进入下一次循环处理。
b)返回 indent 供 pollAll 所在线程处理。
前面提到过 Message的处理是支持两大模式的,其一为及时处理消息,其二为延迟处理消息,而后者又分为两种
a)定延迟事件(sendMessageDelayed)
b)定延迟到的时间点(sendMessageAtTime)
但是最终的实现,都是将事件对应到某一个确定的时间点去处理的,所以实际上的实现就是接口 sendMessageAtTime。
sendMessage
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
sendMessageAtTime(now, handler, message);
需要及时处理的消息,就是将处理的时间点定位为调用时的时间,所以message添加即超时。
void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
const Message& message) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
sendMessageAtTime(now + uptimeDelay, handler, message);
}
对于延时处理,则是将延迟时间加上当前时间点。
SYSTEM_TIME_MONOTONIC 的时间是不包含设备休眠时间的,系统中对其的说明如下
//system\core\libutils\include\utils\Timers.h
enum {
SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock
SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point
SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock
SYSTEM_TIME_THREAD = 3, // high-resolution per-thread clock
SYSTEM_TIME_BOOTTIME = 4, // same as SYSTEM_TIME_MONOTONIC, but including CPU suspend time
};
void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
const Message& message) {
size_t i = 0;
//code 1
size_t messageCount = mMessageEnvelopes.size();
while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) {
i += 1;
}
//code 2
MessageEnvelope messageEnvelope(uptime, handler, message);
mMessageEnvelopes.insertAt(messageEnvelope, i, 1);
//code 3
if (mSendingMessage) {
return;
}
//code 4
if (i == 0) {
wake();
}
}
mMessageEnvelopes的定义如下,其用来存储Message,并且内部是按超期时间由小到大排放的。
//system\core\libutils\include\utils\Looper.h
Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock
此处则是根据当前传入Message的期望处理时间点来定位该Message在mMessageEnvelopes中的处理位置。
MessageEnvelope 则是用来描述一个Message的处理请求,其定义如下
//system\core\libutils\include\utils\Looper.h
struct MessageEnvelope {
MessageEnvelope() : uptime(0) { }
MessageEnvelope(nsecs_t u, sp<MessageHandler> h, const Message& m)
: uptime(u), handler(std::move(h)), message(m) {}
nsecs_t uptime;
sp<MessageHandler> handler;
Message message;
};
uptime
超期时间,即期望被处理的时间点。
handler
Message的回调处理函数,其定义如下。
class MessageHandler : public virtual RefBase {
protected:
virtual ~MessageHandler();
public:
virtual void handleMessage(const Message& message) = 0;
};
MessageHandler则是很简单,只有一个接口 handleMessage 在Message超期时会被回调,在测试程序中对于实现如下
//test\flagstafftest\looper_test\FlagstaffLooperTest.cpp
class FlagstaffMessageHandler:public android::MessageHandler{
public:
void handleMessage(const Message& message) override {
LOG(INFO) <<__func__<<":[message.what]"<<message.what;
}
};
message
message则是消息本身,内部只含一个 int 类型的变量用于区分事件类型。
根据传入Message创建好MessageEnvelope后,将其根据超期时间点插入 mMessageEnvelopes 向量中。
该标志代表当前线程是否正在回调Message的回调函数,即MessageHandler::handleMessage。
如果是第一个Message那么就唤醒下 epoll_wait 来处理下,以提高时效性。
wake
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
实现上则非常简单,因为在Looper初始化的时候就将 mWakeEventFd 句柄添加到epoll轮询队列中去了,此时往里写一个事件,则会唤醒 epoll_wait,当然Message处理完后需要调用 awoken 读走唤醒事件,否则 epoll_wait 会被持续唤醒。
awoken
TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
不论是通过addFd添加的event还是Message的超期处理,都是在 pollInner 中的,对于如何调用到的,这里就不赘述了
pollInner
//code 1
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
if (messageTimeoutMillis >= 0
&& (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
timeoutMillis = messageTimeoutMillis;
}
}
//code 2
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
//code 3
while (mMessageEnvelopes.size() != 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
if (messageEnvelope.uptime <= now) {//code 3-1
...
mMessageEnvelopes.removeAt(0);
mSendingMessage = true;
handler->handleMessage(message);
mSendingMessage = false;
result = POLL_CALLBACK;
} else {//code 3-2
mNextMessageUptime = messageEnvelope.uptime;
break;
}
}
用于确定 epoll_wait的等待时间,比如在上一章的测试代码中是不期望 epoll_wait 超期返回的,但是对于Looper需要支持Message的处理,所以这里是要在两个时间中取小来作为超时事件返回的,否则会影响Message处理的时效性的。
当被加入epoll中的fd被捕捉到对应的监听事件后,epoll_wait 则会返回并且会将 epoll_event 存放到数组 eventItems中。对于Message的唤醒只要满足如下任意条件即可
1) Looper::wake被调用。
2) Message的期望处理时间点超期。
Message的时间点如果否超期,那么就直接调用对应的 handleMessage 来处理Message。
否则更新下下一次对Message来说epoll_wait返回的时间点,待下一次pollInner处理。