美文网首页
[Input] App端消费事件流程

[Input] App端消费事件流程

作者: 尹学姐 | 来源:发表于2023-02-23 22:03 被阅读0次

    这是Android Input系列的第三篇文章,前面两篇的地址如下:

    今天主要讲讲App端在收到事件之后,是如何消费这些事件的。

    首先,我们看一个事件分发的典型Java堆栈:


    image.png

    可以看到,事件是从nativePollOnce分发出来的,调到了InputDispatcherReceiveronReceive方法中,然后再分发给ViewRootImpl去处理。

    今天这篇文章,主要讲一下App端从socket中收到事件后,是怎样调度到InputDispatcherReceiver.onReceive方法的。下一篇文章,我们再讲后续ViewRootImpl的分发流程。

    开始之前,先要要说明的是,接收事件的是App端的主线程,最后分发和处理事件,也是在主线程进行操作。

    之前我们讲MessageQueue的时候说过,主线程会等待在epoll_wait方法,直到监听的端口有内容写入,才会被唤醒,继续执行下面的流程。更详细的内容,可以去看看我之前的文章从epoll机制看MessageQueue

    点击事件的处理流程就是利用的epoll机制,就是我们常说的主线程的Looper机制,下面我们一起来详细看看源码。

    epoll机制监听socketFd

    由前面的分析知道,我们在创建了socket连接后,会创建一个WindowInputEventReceiver对象,并将客户端的InputChannel作为构造函数传入。下面我们就来看看WindowInputEventReceiver的构造方法。

    final class WindowInputEventReceiver extends InputEventReceiver {
        //inputChannel是指socket客户端,Looper是指UI线程的Looper
        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
        }
    }
    

    WindowInputEventReceiver继承自InputEventReceiver

    public InputEventReceiver(InputChannel inputChannel, Looper looper) {
         ...
         mInputChannel = inputChannel;
         mMessageQueue = looper.getQueue(); //UI线程消息队列
         mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                 inputChannel, mMessageQueue);
     }
    

    InputEventReceiver调用的是nativeInit方法,进行初始化

    static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject inputChannelObj, jobject messageQueueObj) {
        sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
                inputChannelObj);
        //获取UI主线程的消息队列
        sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
        //创建NativeInputEventReceiver对象
        sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
                receiverWeak, inputChannel, messageQueue);
        // 调用setFdEvents,将socket连接的fd添加到主线程Looper的监控中
        status_t status = receiver->initialize();
        return reinterpret_cast<jlong>(receiver.get());
    }
    

    nativeInit方法中,最终会调用setFdEvents方法,将socket连接的fd添加到主线程Looper的监控中。socket连接的fd通过InputChannel获取。

    void NativeInputEventReceiver::setFdEvents(int events) {
      if (mFdEvents != events) {
          mFdEvents = events;
          int fd = mInputConsumer.getChannel()->getFd();
          if (events) {
              //将socket客户端的fd添加到主线程的消息池
              mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
          } else {
              mMessageQueue->getLooper()->removeFd(fd);
          }
      }
    }  
    

    addFd方法,就是通过epoll_ctl将fd加入监听,同时构造一个Request对象,将它加到mRequests队列中。

    int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
        {
           // 构造request
            Request request;
            request.fd = fd;
            request.ident = ident;
            request.events = events;
            request.seq = mNextRequestSeq++;
            request.callback = callback; //是指nativeInputEventReceiver
            request.data = data;
            // 构造eventItem
            struct epoll_event eventItem;
            request.initEventItem(&eventItem);
            ssize_t requestIndex = mRequests.indexOfKey(fd);
            if (requestIndex < 0) {
                //通过epoll监听fd
                int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
                //该fd的request加入到mRequests队列
                mRequests.add(fd, request); 
            } else {
                int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
                mRequests.replaceValueAt(requestIndex, request);
            }
        } 
        return 1;
    }
    

    我们来看看Request的结构:

    • fd:存的是socket通信的fd
    • ident:0
    • events:ALOOPER_EVENT_INPUT
    • callback:就是nativeInputEventReceiver

    epoll_wait被唤醒

    当监听的socket收到数据时,会从pollInner方法唤醒主线程Looper处理消息。

    int Looper::pollInner(int timeoutMillis) {
        mPolling = true; //即将处于idle状态
        struct epoll_event eventItems[EPOLL_MAX_EVENTS]; //fd最大个数为16
    
        //等待事件发生或者超时,在nativeWake()方法,向管道写端写入字符;
        int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
        mPolling = false; //不再处于idle状态
        //循环遍历,处理所有的事件
        for (int i = 0; i < eventCount; i++) {
            int fd = eventItems[i].data.fd;
            uint32_t epollEvents = eventItems[i].events;
            // 如果是从wakeEventFd唤醒,表示MessageQueue有新消息了,会往这个fd写入
            if (fd == mWakeEventFd) {
                if (epollEvents & EPOLLIN) {
                    // 这个方法会读取mWakeEventFd上的所有数据
                    awoken(); 
                }
            } else {
                ssize_t requestIndex = mRequests.indexOfKey(fd);
                if (requestIndex >= 0) {
                    //处理request,生成对应的reponse对象,push到mResponses数组
                    pushResponse(events, mRequests.valueAt(requestIndex));
                }
            }
        }
    Done: ;
        //处理带有Callback()方法的Response事件,执行Reponse相应的回调方法
        for (size_t i = 0; i < mResponses.size(); i++) {
            Response& response = mResponses.editItemAt(i);
            if (response.request.ident == POLL_CALLBACK) {
                int fd = response.request.fd;
                int events = response.events;
                void* data = response.request.data;
                // 处理请求的回调方法
                int callbackResult = response.request.callback->handleEvent(fd, events, data);
                // 正常处理事件会返回1,如果返回0,表示窗口被移除
                if (callbackResult == 0) {
                    removeFd(fd, response.request.seq); //移除fd
                }
                response.request.callback.clear(); //清除reponse引用的回调方法
                result = POLL_CALLBACK;  // 发生回调
            }
        }
        return result;
    }
    

    这个方法的流程:

    • 获取唤醒的fd和events,从mRequest中找到对应fd的request
    • 将events和request封装成一个response对象,然后将他加到mResponses数组中
    • 循环处理mResponses数组中的所有response,调用request.callback->handleEvent

    这个方法会调到request.callback->handleEvent,也就是NativeInputEventReceiverhandleEvent方法。这个方法主要调用了consumeEvents,所以我们直接看ConsumeEvents方法。

    读取并消费所有的message

    status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
            bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
        // 循环消费所有的消息
        for (;;) {
            status_t status = mInputConsumer.consume(&mInputEventFactory,
                    consumeBatches, frameTime, &seq, &inputEvent);
                if (inputEventObj) {
                    //执行Java层的InputEventReceiver.dispachInputEvent
                    env->CallVoidMethod(receiverObj.get(),
                            gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
                } else {
                    skipCallbacks = true;
                }
            }
            if (skipCallbacks) {
                //发生异常,则直接向InputDispatcher线程发送完成信号。
                mInputConsumer.sendFinishedSignal(seq, false);
            }
        }
    }
    

    循环读取所有的消息,并且调用Java层的InputEventReceiver.dispatchInputEvent方法处理事件。

    status_t InputConsumer::consume(InputEventFactoryInterface* factory,
            bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
        //循环遍历所有的Event
        while (!*outEvent) {
            if (mMsgDeferred) {
                mMsgDeferred = false; //上一次没有处理的消息
            } else {
                // 通过InputChannel接收一条消息
                status_t result = mChannel->receiveMessage(&mMsg);
                if (result) {
                    if (consumeBatches || result != WOULD_BLOCK) {
                        result = consumeBatch(factory, frameTime, outSeq, outEvent);
                    }
                }
            }
            }
        }
        return OK;
    }
    

    单条消息,调用InputChannel的receiveMessage读取。

    status_t InputChannel::receiveMessage(InputMessage* msg) {
        ssize_t nRead;
        do {
            //读取InputDispatcher发送过来的消息
            nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
        } while (nRead == -1 && errno == EINTR);
        return OK;
    }
    

    Java端收到事件后,会回调到WindowInputEventReceiveronInputEvent方法中,处理事件。

    发送处理完信号

    当事件处理完后,会调用finishInputEvent方法,将处理完的结果返回给系统。

    从Java端的InputEventReceiver开始。

    public final void finishInputEvent(InputEvent event, boolean handled) {
                int seq = mSeqMap.valueAt(index);
                mSeqMap.removeAt(index);
                // 调用native的方法
                nativeFinishInputEvent(mReceiverPtr, seq, handled);
    }
    

    调用到NativeInputEventReceiver,最终调用到sendFinishedSignal,然后调用到sendUnchainedFinishedSignal

    status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
        // 组装一个finished的信号,通过InputChannel的socket发送给系统
        InputMessage msg;
        msg.header.type = InputMessage::TYPE_FINISHED;
        msg.body.finished.seq = seq;
        msg.body.finished.handled = handled;
        return mChannel->sendMessage(&msg);
    }
    

    总结

    App端消费事件的流程如下:

    • 在创建完socketpair后,App端会用mInputChannel创建一个WindowInputEventReceiver对象,并且注册对socket fd的监听。
    • socket fd上有输入写入时(即有事件时),会唤醒主线程
    • 主线程循环读取socket fd上的InputMessage,然后将message发送给Java层的InputEventReceiver去处理
    • 处理完之后,组装一个finished的信号,通过mInputChannel发送给system_server

    相关文章

      网友评论

          本文标题:[Input] App端消费事件流程

          本文链接:https://www.haomeiwen.com/subject/jleokdtx.html