Android T(13)-- Looper 的实现(二)

1. 概述

  上一章关注的时Looper的应用,本章则聚焦于其实现。诚然 AOSP 是开源的,且它的文档相较其他个人开源项目来说已经够有诚意了,但还是不如API文档那般事无巨细的说明。所以使用Android提供的轮子前,还是要扒开看看它内部的。

2.实例的获取–Looper::prepare

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);
    }
}

2.1 Looper 对无回调方法的监听支持 – code 1

  在调用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) {
				...
			}
		}
	});

  

2.1 初始化Looper内部监听状态 – code 2

  在一个线程中第一次调用Looper::getForThread的时候,其必然是nullptr的,所以这里的流程则是构造Looper,如下则是对应的实现代码

Looper::Looper(bool allowNonCallbacks)
    : mAllowNonCallbacks(allowNonCallbacks),
    {
    ...
    rebuildEpollLocked();
}

  rebuildEpollLocked 则由如下步骤组成
    a)更新epoll fd,因为rebuildEpollLocked在Looper请求重建内部epoll句柄的时候会被再次调用。
    b)创建一个 epoll_event ,并将其加入 epoll 的监听队列中,其依赖的fd如下,作用则是用于后面即将介绍的 <Message方式监听>实现。

//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);
}

2.1 保证线程中Looper的单实例 – code 3

  就是对 pthread_setspecific/pthread_getspecific 家族的封装,目的则是实现资源在线程中的单例性。

3.监听给定 fd(file descriptor)–Looper::addFd

  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;
};

3.1 addFd的实现代码

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;
}
3.1.1 code1–ident 的确定

  如果创建Looper的时候没有指定标志PREPARE_ALLOW_NON_CALLBACKS,那么在添加监听fd的时候是不允许回调类或者方法为空的。所以对 mAllowNonCallbacks 进行了判断。反之提供了回调类或方法,这意味着本次的请求为 POLL_CALLBACK, 在后续线程调用pollAll过程中是不会退出并返回ident值的,而是直接调用给定的回调类或方法去处理。

3.1.2 request 的唯一标志–code 2

  用来统计requestion的数量,当然也作为每一个requestion的唯一标识符。

3.1.3 创建 Request 并监听对应的fd–code 3

  在Looper中,使用Request来封装每一个请求,其内部记录了被轮询的文件句柄(fd)、唯一标识符(indent)、epoll监听的事件(event)以及该fd产生event的处理方法(callback)等。然后根据传入的fd及seq创建一个epoll_event,待epoll_wait返回时使用。

3.1.4 记录 Request并监听对应的fd–code 4

  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的监听队列中去了。

3.2 Request 的处理

  在上一章节也提到过,事件的处理线程实际上就是调用 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;
3.2.1 epoll_wait – code 1

  当被加入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从而进行处理。

3.2.2 创建 Response – code 2

  在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。

3.2.3 处理Response – code 3

  满足 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);
3.2.4 结果返回 – code 4

  返回值只有下面两种情况
    a)POLL_CALLBACK,且pollAll入参为-1,那么pollAll是不会返回的,直接进入下一次循环处理。
    b)返回 indent 供 pollAll 所在线程处理。

5 Message 事件的处理

  前面提到过 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
};

5.1 sendMessageAtTime 的实现

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();
    }
}
5.1.1 定位Message的存储位置 – code 1

  mMessageEnvelopes的定义如下,其用来存储Message,并且内部是按超期时间由小到大排放的。

//system\core\libutils\include\utils\Looper.h
Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock

  此处则是根据当前传入Message的期望处理时间点来定位该Message在mMessageEnvelopes中的处理位置。

5.1.2 code 2

  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 向量中。

5.1.3 mSendingMessage – code 3

  该标志代表当前线程是否正在回调Message的回调函数,即MessageHandler::handleMessage。

5.1.4 code 4

  如果是第一个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)));

5.2 Message 的处理

  不论是通过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;
        }
    }
5.2.1 epoll_wait timeout事件确定 – code 1

  用于确定 epoll_wait的等待时间,比如在上一章的测试代码中是不期望 epoll_wait 超期返回的,但是对于Looper需要支持Message的处理,所以这里是要在两个时间中取小来作为超时事件返回的,否则会影响Message处理的时效性的。

5.2.2 epoll_wait – code 2

  当被加入epoll中的fd被捕捉到对应的监听事件后,epoll_wait 则会返回并且会将 epoll_event 存放到数组 eventItems中。对于Message的唤醒只要满足如下任意条件即可
    1) Looper::wake被调用。
    2) Message的期望处理时间点超期。

5.2.3 handleMessage的回调 – code 3

  Message的时间点如果否超期,那么就直接调用对应的 handleMessage 来处理Message。
  否则更新下下一次对Message来说epoll_wait返回的时间点,待下一次pollInner处理。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值