• 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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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) {
    				...
    			}
    		}
    	});
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

      

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

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

    Looper::Looper(bool allowNonCallbacks)
        : mAllowNonCallbacks(allowNonCallbacks),
        {
        ...
        rebuildEpollLocked();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

      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
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

        创建的 epoll_event 如下

    epoll_event wakeEvent = createEpollEvent(EPOLLIN, WAKE_EVENT_FD_SEQ);
        return {.events = events, .data = {.u64 = seq}};
    
    • 1
    • 2

        加入 epoll监听队列

    int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &wakeEvent);
    
    • 1

        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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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);
    
    • 1
    • 2
    • 3
    • 4

      第一种传入的回调可以是函数指针,但是实现中还是会将函数指针封装成类 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;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    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
    
    • 1
    • 2
    • 3

      从两个map的申明来看,可以看到如下映射关系

    fd --> seq --> Request
    
    • 1

      在添加一个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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

      我们默认入参-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)
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

       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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    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 */
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

      前面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;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

      记录了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;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

      对于 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);
    
    • 1
    • 2
    • 3
    • 4

    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);
    
    • 1
    • 2
    • 3

      需要及时处理的消息,就是将处理的时间点定位为调用时的时间,所以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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

      对于延时处理,则是将延迟时间加上当前时间点。
      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
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    5.1.1 定位Message的存储位置 – code 1

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

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

      此处则是根据当前传入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;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

      uptime
        超期时间,即期望被处理的时间点。
      handler
        Message的回调处理函数,其定义如下。

    class MessageHandler : public virtual RefBase {
    protected:
        virtual ~MessageHandler();
    
    public:
        virtual void handleMessage(const Message& message) = 0;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

        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;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

      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)));
    
    • 1
    • 2

      实现上则非常简单,因为在Looper初始化的时候就将 mWakeEventFd 句柄添加到epoll轮询队列中去了,此时往里写一个事件,则会唤醒 epoll_wait,当然Message处理完后需要调用 awoken 读走唤醒事件,否则 epoll_wait 会被持续唤醒。

    awoken
        TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
    
    • 1
    • 2

    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;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    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处理。

  • 相关阅读:
    html移动端实现手写签名,signature手写签名实现,微信公众号浏览器html手写签名实现
    k8s-Pod调度
    低代码如何提升开发人员的开发能力
    飞书API 2-1:如何通过 API 创建文件夹?
    基于Springboot的漫画网站springboot022
    【python】
    JavaSE运算符
    ikuai路由器通过curl模拟功能操作
    java小游戏-超级玛丽
    做个小工具显示UE里地形的高度图和权重图
  • 原文地址:https://blog.csdn.net/u014787262/article/details/126064855