• Android finishInputEvent 流程分析


    InputDispatcher将事件分发给应用程序后,将该event放入connection的wq队列中,APP处理完之后,需要发送一个完成的信号给InputDispatcher,以供InputDispatcher来将该event从wq队列中移除。我们来分析这个过程。

    我们知道,事件在应用端的的处理过程是经过一系列的InputStage处理,当这些InputStage都处理完毕(不消费也算处理完毕)后,就会调用finishInputEvent来通知InputDispatcher。从finishInputEvent开始分析

    //frameworks/base/core/java/android/view/ViewRootImpl.java
    private void finishInputEvent(QueuedInputEvent q) {
            Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
                    q.mEvent.getSequenceNumber());
    
            if (q.mReceiver != null) {
                boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
                q.mReceiver.finishInputEvent(q.mEvent, handled);//1
            } else {
                q.mEvent.recycleIfNeededAfterDispatch();
            }
    
            recycleQueuedInputEvent(q);
        }
    

    注释1处, q.mReceiver为InputEventReceiver对象,调用其finishInputEvent方法

    //frameworks/base/core/java/android/view/InputEventReceiver.java
    public final void finishInputEvent(InputEvent event, boolean handled) {
            if (event == null) {
                throw new IllegalArgumentException("event must not be null");
            }
            if (mReceiverPtr == 0) {
                Log.w(TAG, "Attempted to finish an input event but the input event "
                        + "receiver has already been disposed.");
            } else {
                int index = mSeqMap.indexOfKey(event.getSequenceNumber());
                if (index < 0) {
                    Log.w(TAG, "Attempted to finish an input event that is not in progress.");
                } else {
                    int seq = mSeqMap.valueAt(index);//取出seq
                    mSeqMap.removeAt(index);
                    nativeFinishInputEvent(mReceiverPtr, seq, handled);//1,native方法
                }
            }
            event.recycleIfNeededAfterDispatch();
        }
    

    先取出seq,这个seq是之前InputDispatcher发送事件的时候传递过来的的,后续可以通过seq在InputDispatcher中找到对应的event。注释1处调用native方法

    //frameworks/base/core/jni/android_view_InputEventReceiver.cpp
    status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
     //省略
        status_t status = mInputConsumer.sendFinishedSignal(seq, handled);//发送完成的信号
      //省略
        return status;
    }
    

    通过sendFinishedSignal来向InputDispatcher发送事件处理完毕的消息

    //frameworks/native/libs/input/InputTransport.cpp
    status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
    //省略
    
        // Send finished signal for the last message in the batch.
        return sendUnchainedFinishedSignal(seq, handled);
    }
    
    status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
        InputMessage msg;
        msg.header.type = InputMessage::TYPE_FINISHED;
        msg.body.finished.seq = seq;
        msg.body.finished.handled = handled;
        return mChannel->sendMessage(&msg);
    }
    

    可以看出,通过InputChanel发送消息

    //frameworks/base/core/jni/android_view_InputEventReceiver.cpp
    status_t InputChannel::sendMessage(const InputMessage* msg) {
        size_t msgLength = msg->size();
        ssize_t nWrite;
        do {
            nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
        } while (nWrite == -1 && errno == EINTR);
    

    向fd写入数据,那么InputDispatcher那边的fd就能接收到数据了,然后调用handleReceiveCallback。关于这部分,Android 11 输入系统之InputDispatcher和应用窗口建立联系一文中已经分析过了

    //frameworks/native/services/inputflinger/InputDispatcher.cpp
    int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
        InputDispatcher* d = static_cast<InputDispatcher*>(data);//取出InputDispatcher对象
    
        { // acquire lock
            AutoMutex _l(d->mLock);
    
            ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd);//根据fd取出index
            if (connectionIndex < 0) {
                ALOGE("Received spurious receive callback for unknown input channel.  "
                        "fd=%d, events=0x%x", fd, events);
                return 0; // remove the callback
            }
    
            bool notify;
            sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex);//取出connection
            if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
               
    		//省略
                nsecs_t currentTime = now();
                bool gotOne = false;
                status_t status;
                for (;;) {
                    uint32_t seq;
                    bool handled;
                    status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);//1
                    if (status) {
                        break;
                    }
                    d->finishDispatchCycleLocked(currentTime, connection, seq, handled);//2
                    gotOne = true;
                }
                if (gotOne) {
                    d->runCommandsLockedInterruptible();
                    if (status == WOULD_BLOCK) {
                        return 1;
                    }
                }
    
                notify = status != DEAD_OBJECT || !connection->monitor;
                if (notify) {
                    ALOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",
                            connection->getInputChannelName().c_str(), status);
                }
            } else {
                // Monitor channels are never explicitly unregistered.
                // We do it automatically when the remote endpoint is closed so don't warn
                // about them.
                notify = !connection->monitor;
                if (notify) {
                    ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred.  "
                            "events=0x%x", connection->getInputChannelName().c_str(), events);
                }
            }
    
            // Unregister the channel.
            d->unregisterInputChannelLocked(connection->inputChannel, notify);
            return 0; // remove the callback
        } // release lock
    }
    

    注释1处接收消息,得到seq。注释2处会调用onDispatchCycleFinishedLocked,向Commands队列中放入命令

    //frameworks/native/services/inputflinger/InputDispatcher.cpp
    void InputDispatcher::onDispatchCycleFinishedLocked(
            nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) {
        CommandEntry* commandEntry = postCommandLocked(
                & InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
        commandEntry->connection = connection;
        commandEntry->eventTime = currentTime;
        commandEntry->seq = seq;
        commandEntry->handled = handled;
    }
    

    队列执行时调用doDispatchCycleFinishedLockedInterruptible方法

    void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
            CommandEntry* commandEntry) {
        sp<Connection> connection = commandEntry->connection;
        nsecs_t finishTime = commandEntry->eventTime;
        uint32_t seq = commandEntry->seq;//得到seq
        bool handled = commandEntry->handled;
    
        // Handle post-event policy actions.
        DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);//根据seq从wq中找出之前分发的事件
        if (dispatchEntry) {
           //省略
    
            bool restartEvent;
            if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
                KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
                restartEvent = afterKeyEventLockedInterruptible(connection,
                        dispatchEntry, keyEntry, handled);
            } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) {
                MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
                restartEvent = afterMotionEventLockedInterruptible(connection,
                        dispatchEntry, motionEntry, handled);//这个方法也是直接返回false
            } else {
                restartEvent = false;
            }
    
            // Dequeue the event and start the next cycle.
            // Note that because the lock might have been released, it is possible that the
            // contents of the wait queue to have been drained, so we need to double-check
            // a few things.
            if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
                connection->waitQueue.dequeue(dispatchEntry);//从wq队列中将事件移除
                traceWaitQueueLengthLocked(connection);
         	//省略
    
            // Start the next dispatch cycle for this connection.
            startDispatchCycleLocked(now(), connection);//下一轮的派发
        }
    }
    

    可以看出,当应用端事件处理完毕之后,需要告知InputDispatcher,InputDispatcher收到完成的消息后,会将该事件从wq队列中移除,然后进行下一轮事件的派发

    总结

    finishInputEvent的工作原理
    在这里插入图片描述

  • 相关阅读:
    Windows bat脚本启动jar包(亲测有效),,监控端口,如果没有,就启动jar包,自动退出cmd框
    OpenSea PHP开发包
    悄然而至,低/无代码的出现真能撬动传统开发的蛋糕?
    一个可以加密的PDF文档二维码,如何制作?
    docker 安装minio 一脚shell脚本
    运筹学:影子价格(shadow price)和对偶价格(dual price)
    qsort函数详细讲解以及利用冒泡排序模拟实现qsort函数
    【Vue】模块基本语法「上篇」
    博俊转债上市价格预测
    CodeQL使用流程
  • 原文地址:https://blog.csdn.net/littleyards/article/details/138771864