美文网首页程序员Android开发Android 技术开发
Android触摸事件的传递(五)--输入系统-InputCha

Android触摸事件的传递(五)--输入系统-InputCha

作者: 凯玲之恋 | 来源:发表于2018-07-22 01:49 被阅读334次

    了解更多,移步Android触摸事件传递机制系列详解

    • InputReaderThreadInputDispatcherThread是运行在SystemServer进程中的
    • 我们的应用进程是和其不在同一个进程中的
    • 这之间一定也是有进程间的通信机制在里面

    1 InputChannel的创建

    • InputChannel的创建是在 ViewRootImplsetView方法中。
    • 首先是创建了一个InputChannel,然后将其调用了WindowSession的addToDisplay方法将其作为参数传递。
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        ....
      if ((mWindowAttributes.inputFeatures
                            & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
           //首先是创建了一个InputChannel
           mInputChannel = new InputChannel();
       }
      ....
      //将InputChannel添加到WindowManagerService中创建socketpair(一对socket)用来发送和接受事件
      res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                getHostVisibility(), mDisplay.getDisplayId(),
                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                mAttachInfo.mOutsets, mInputChannel);
      ....
    
    //开启了对于InputChannel中输入事件的监听
    if (mInputChannel != null) {
       if (mInputQueueCallback != null) {
           mInputQueue = new InputQueue();
           mInputQueueCallback.onInputQueueCreated(mInputQueue);
      }
       mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                                Looper.myLooper());
    }
    
    
      ...
    }
    

    2 将InputChannel添加到WindowManagerService中创建socketpair(一对socket)用来发送和接受事件

    • addToDisplay将会把InputChannel添加到WindowManagerService中。会调用WMSaddWindow方法。
    • 对于InputChannel的相关处理调用了WindowStateopenInputChannel方法。
     public int addWindow(Session session, IWindow client, int seq,
                WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
                Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
                InputChannel outInputChannel) {
          ....
    
          final boolean openInputChannels = (outInputChannel != null
                        && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
          if  (openInputChannels) {
              win.openInputChannel(outInputChannel);
          }
          ....
    }
    
    void openInputChannel(InputChannel outInputChannel) {
        if (mInputChannel != null) {
            throw new IllegalStateException("Window already has an input channel.");
         }
         String name = makeInputChannelName();
         InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
         mInputChannel = inputChannels[0];
         mClientChannel = inputChannels[1];
         mInputWindowHandle.inputChannel = inputChannels[0];
         if (outInputChannel != null) {
           mClientChannel.transferTo(outInputChannel);
           mClientChannel.dispose();
           mClientChannel = null;
          } else {
             mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
          }
           //`InputChannel`设置到`InputDispatcher`
           mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
    }
    
    • 首先调用了InputChannelopenInputChannelPair方法,该方法调用了InputChannel的native方法nativeOpenInputChannelPair,创建了两个InputChannel,对其中一个通过InputManager进行了InputChannel的注册。
    • 对于InputChannel的相关Native的实现是在InputTransport中,nativeOpenInputChannelPair的源码如下
    status_t InputChannel::openInputChannelPair(const String8& name,
            sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
        int sockets[2];
        if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
            status_t result = -errno;
            outServerChannel.clear();
            outClientChannel.clear();
            return result;
        }
    
        int bufferSize = SOCKET_BUFFER_SIZE;
        setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
        setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
        setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
        setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
    
        String8 serverChannelName = name;
        serverChannelName.append(" (server)");
        outServerChannel = new InputChannel(serverChannelName, sockets[0]);
    
        String8 clientChannelName = name;
        clientChannelName.append(" (client)");
        outClientChannel = new InputChannel(clientChannelName, sockets[1]);
        return OK;
    }
    
    • 注:Linux实现了一个源自BSD的socketpair调用可以实现上述在同一个文件描述符中进行读写的功能(该调用目前也是POSIX规范的一部分 。该系统调用能创建一对已连接的(UNIX族)无名socket。在Linux中,完全可以把这一对socket当成pipe返回的文件描述符一样使用,唯一的区别就是这一对文件描述符中的任何一个都可读和可写。
    • socketpair产生的文件描述符是一对socketsocket上的标准操作都可以使用,其中也包括shutdown。——利用shutdown,可以实现一个半关闭操作,通知对端本进程不再发送数据,同时仍可以利用该文件描述符接收来自对端的数据。
    • sendMessage发送消息
    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);
         .....
        return OK;
    }
    
    • 接收消息,通过读socket的方式来读取消息。
    status_t InputChannel::receiveMessage(InputMessage* msg) {
        ssize_t nRead;
        do {
            nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
        } while (nRead == -1 && errno == EINTR);
        ......
        return OK;
    }
    
    

    3 开启输入事件的监听接受事件

    • 之前的setView中,我们创建了InputChannel之后,开启了对于InputChannel中输入事件的监听。
    if (mInputChannel != null) {
       if (mInputQueueCallback != null) {
           mInputQueue = new InputQueue();
           mInputQueueCallback.onInputQueueCreated(mInputQueue);
      }
       mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                                Looper.myLooper());
    }
    

    WindowInputEventReceiver的构造函数如下,其继承自InputEventReceiver

    final class WindowInputEventReceiver extends InputEventReceiver {
         public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
             super(inputChannel, looper);
         }
          ....
    }
    
    • InputEventReceiver的构造函数源码如下
    public InputEventReceiver(InputChannel inputChannel, Looper looper) {
         ....
         mInputChannel = inputChannel;
         mMessageQueue = looper.getQueue();
         mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                    inputChannel, mMessageQueue);
      }
    

    这里调用了native方法来做初始化,相关的native方法的实现在android_view_InputEventReceiver.cpp

    static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
            jobject inputChannelObj, jobject messageQueueObj) {
       ....
      sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
                inputChannelObj);
      sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
      sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
                receiverWeak, inputChannel, messageQueue);
        status_t status = receiver->initialize();
      .....
    }
    
    • 根据传入的InputChannelMessageQueue,创建一个NativeInputEventReceiver,然后调用其initialize方法。
    status_t NativeInputEventReceiver::initialize() {
        setFdEvents(ALOOPER_EVENT_INPUT);
        return OK;
    }
    

    initialize()方法中,只调用了一个函数setFdEvents

    void NativeInputEventReceiver::setFdEvents(int events) {
        if (mFdEvents != events) {
            mFdEvents = events;
            int fd = mInputConsumer.getChannel()->getFd();
            if (events) {
                mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
            } else {
                mMessageQueue->getLooper()->removeFd(fd);
            }
        }
    }
    
    • InputConsumer中获取到channel的fd,然后调用LooperaddFd方法。
    int ALooper_addFd(ALooper* looper, int fd, int ident, int events,
            ALooper_callbackFunc callback, void* data) {
        return ALooper_to_Looper(looper)->addFd(fd, ident, events, callback, data);
    }
    

    Looper的addFd的实现如下

    int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
        Request request;
        request.fd = fd;
        request.ident = ident;
        request.events = events;
        request.seq = mNextRequestSeq++;
        request.callback = callback;
         request.data = data;
         if (mNextRequestSeq == -1) mNextRequestSeq = 0;
         struct epoll_event eventItem;
         request.initEventItem(&eventItem);
         ssize_t requestIndex = mRequests.indexOfKey(fd);
          if (requestIndex < 0) {
              int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
              if (epollResult < 0) {
                    return -1;
                }
             mRequests.add(fd, request);
           } 
    }
    

    ···

    • 注:epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
      ···

    • 该方法所执行的操作就是对传递的fd添加epoll监控,Looper会循环调用pollOnce方法,而pollOnce方法的核心实现就是pollInner

    • 其代码大致实现内容为等待消息的到来,当有消息到来后,根据消息类型做一些判断处理,然后调用其相关的callback。

    • 我们当前是对于开启的socket的一个监听,当有数据到来,我们便会执行相应的回调。这里对于InputChannel的回调是在调用了NativeInputEventReceiverhandleEvent方法。

    4 处理事件 handleEvent--事件读出来

    int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
        if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
            return 0;  //移除窗口或者IME对话框, 则移除该事件
        }
    
        if (events & ALOOPER_EVENT_INPUT) {
            JNIEnv* env = AndroidRuntime::getJNIEnv();
            //【见小节3.3】
            status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
            mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
            return status == OK || status == NO_MEMORY ? 1 : 0;
        }
    
        if (events & ALOOPER_EVENT_OUTPUT) {
            for (size_t i = 0; i < mFinishQueue.size(); i++) {
                const Finish& finish = mFinishQueue.itemAt(i);
                //【见小节3.4】
                status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
                if (status) {
                    mFinishQueue.removeItemsAt(0, i);
    
                    if (status == WOULD_BLOCK) {
                        return 1; //保留callback,稍后重试
                    }
    
                    if (status != DEAD_OBJECT) {
                        JNIEnv* env = AndroidRuntime::getJNIEnv();
                        String8 message;
                        message.appendFormat("Failed to finish input event. status=%d", status);
                        jniThrowRuntimeException(env, message.string());
                        mMessageQueue->raiseAndClearException(env, "finishInputEvent");
                    }
                    return 0; //移除callback
                }
            }
            mFinishQueue.clear();
            setFdEvents(ALOOPER_EVENT_INPUT);
            return 1;
        }
        return 1;
    }
    
    • 对于Event的处理,这里调用consumeEvents来对事件进行处理。
    • 1.调用consume来进行接受事件
      1. 执行Java层的InputEventReceiver.dispachInputEvent
    status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
            bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
        ...
    
        ScopedLocalRef<jobject> receiverObj(env, NULL);
        bool skipCallbacks = false;
        for (;;) {
            uint32_t seq;
            InputEvent* inputEvent;
            //consume 处理(接收事件)
            status_t status = mInputConsumer.consume(&mInputEventFactory,
                    consumeBatches, frameTime, &seq, &inputEvent);
            if (status) {
                if (status == WOULD_BLOCK) {
                    ...
                    return OK; //消费完成
                }
                return status; //消失失败
            }
    
            if (!skipCallbacks) {
                if (!receiverObj.get()) {
                    receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
                    if (!receiverObj.get()) {
                        return DEAD_OBJECT;
                    }
                }
    
                jobject inputEventObj;
                switch (inputEvent->getType()) {
                    case AINPUT_EVENT_TYPE_KEY:
                        //由Native的inputEvent来生成Java层的事件
                        inputEventObj = android_view_KeyEvent_fromNative(env,
                                static_cast<KeyEvent*>(inputEvent));
                        break;
                    ...
                }
    
                if (inputEventObj) {
                    //执行Java层的InputEventReceiver.dispachInputEvent
                    env->CallVoidMethod(receiverObj.get(),
                            gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
                    if (env->ExceptionCheck()) {
                        skipCallbacks = true; //分发过程发生异常
                    }
                    env->DeleteLocalRef(inputEventObj);
                } else {
                    skipCallbacks = true;
                }
            }
    
            if (skipCallbacks) {
                //发生异常,则直接向InputDispatcher线程发送完成信号。
                mInputConsumer.sendFinishedSignal(seq, false);
            }
        }
    }
    
    status_t InputConsumer::consume(InputEventFactoryInterface* factory,
            bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
    
        *outSeq = 0;
        *outEvent = NULL;
    
        //循环遍历所有的Event
        while (!*outEvent) {
            if (mMsgDeferred) {
                mMsgDeferred = false; //上一次没有处理的消息
            } else {
                //收到新消息【见小节3.3.2】
                status_t result = mChannel->receiveMessage(&mMsg);
                if (result) {
                    if (consumeBatches || result != WOULD_BLOCK) {
                        result = consumeBatch(factory, frameTime, outSeq, outEvent);
                        if (*outEvent) {
                            break;
                        }
                    }
                    return result;
                }
            }
    
            switch (mMsg.header.type) {
              case InputMessage::TYPE_KEY: {
                  //从mKeyEventPool池中取出KeyEvent
                  KeyEvent* keyEvent = factory->createKeyEvent();
                  if (!keyEvent) return NO_MEMORY;
    
                  //将msg封装成KeyEvent
                  initializeKeyEvent(keyEvent, &mMsg);
                  *outSeq = mMsg.body.key.seq;
                  *outEvent = keyEvent;
                  break;
              }
              ...
            }
        }
        return OK;
    }
    
    • 调用consume方法会持续的调用InputChannelreceiveMessage方法来从socket中读取数据。到这里,我们已经将写入socket的事件读出来了。
    • InputChannel在创建之后,通过为其InputEventReceiver对其fd进行epoll监控,当有变动的时候,调用InputChannel来接收消息。
      [-> InputTransport.cpp]
    status_t InputChannel::receiveMessage(InputMessage* msg) {
        ssize_t nRead;
        do {
            //读取InputDispatcher发送过来的消息
            nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
        } while (nRead == -1 && errno == EINTR);
    
        if (nRead < 0) {
            int error = errno;
            if (error == EAGAIN || error == EWOULDBLOCK) {
                return WOULD_BLOCK;
            }
            if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED) {
                return DEAD_OBJECT;
            }
            return -error;
        }
    
        if (nRead == 0) {
            return DEAD_OBJECT;
        }
    
        if (!msg->isValid(nRead)) {
            return BAD_VALUE;
        }
    
        return OK;
    }
    

    5 传递给ViewRootImpl

    InputEventReceiver.dispachInputEvent
    [-> InputEventReceiver.java]

    private void dispatchInputEvent(int seq, InputEvent event) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event);
    }
    

    onInputEvent
    [-> ViewRootImpl.java ::WindowInputEventReceiver]

    final class WindowInputEventReceiver extends InputEventReceiver {
        public void onInputEvent(InputEvent event) {
           enqueueInputEvent(event, this, 0, true);
        }
        ...
    }
    

    enqueueInputEvent

    • enqueueInputEvent方法从InputEventReceiver中获取到InputEvent,然后将其加入到当前的事件队列之中,最后调用doProcessInputEvents来进行处理。
    void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) {
        adjustInputEventForCompatibility(event);
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
    
        QueuedInputEvent last = mPendingInputEventTail;
        if (last == null) {
            mPendingInputEventHead = q;
            mPendingInputEventTail = q;
        } else {
            last.mNext = q;
            mPendingInputEventTail = q;
        }
        mPendingInputEventCount += 1;
    
        if (processImmediately) {
            doProcessInputEvents(); //doProcessInputEvents
        } else {
            scheduleProcessInputEvents();
        }
    }
    

    doProcessInputEvents

    • 遍历所有的消息,如果事件类型为触摸屏事件,对其进行相应的时间修改,最后对于每一个处理完成的事件调用deliverInputEvent,

    [-> ViewRootImpl.java]

    void doProcessInputEvents() {
        while (mPendingInputEventHead != null) {
            QueuedInputEvent q = mPendingInputEventHead;
            mPendingInputEventHead = q.mNext;
            if (mPendingInputEventHead == null) {
                mPendingInputEventTail = null;
            }
            q.mNext = null;
    
            mPendingInputEventCount -= 1;
    
            long eventTime = q.mEvent.getEventTimeNano();
            long oldestEventTime = eventTime;
            ...
            mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
            //[见小节3.3.7]
            deliverInputEvent(q);
        }
    
        if (mProcessInputEventsScheduled) {
            mProcessInputEventsScheduled = false;
            mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
        }
    }
    

    事件分发deliverInputEvent

    • 首先进行事件的一个判断,通过shouldSkipIme来判断是否传递给输入法,然后决定使用何种InputStage进行消息的继续传递,这里实现了多种InputStage,对于每一个类型的InputStage都实现了一个方法process方法来针对不同类型的事件做处理,如果是触摸屏类的消息,最终会将事件的处理转交到View的身上。

    [-> ViewRootImpl.java]

    private void deliverInputEvent(QueuedInputEvent q) {
         if (mInputEventConsistencyVerifier != null) {
             mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
         }
    
         InputStage stage;
         if (q.shouldSendToSynthesizer()) {
             stage = mSyntheticInputStage;
         } else {
             stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
         }
    
         if (stage != null) {
             stage.deliver(q);
         } else {
             finishInputEvent(q); //[见小节3.4]
         }
     }
    

    经过一系列的InputStage调用, 最终会分发到真正需要处理该时间的窗口. 当处理完后会调用finishInputEvent(),

    总结

    至此对于从硬件设备产生数据,到数据被逐层传递到应用程序中的整个流程就梳理完了。事件相关的创建,传递流程如下所示。


    4fc1c74f1eadccf675bb560d15223dca.png
    input_summary.jpg

    6 前传InputChannel又是如何被设置到InputDispatcher

    public void registerInputChannel(InputChannel inputChannel,
                InputWindowHandle inputWindowHandle) {
       if (inputChannel == null) {
          throw new IllegalArgumentException("inputChannel must not be null.");
       }
       nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
    }
    
    static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
            jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
        NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    
        sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
                inputChannelObj);
        if (inputChannel == NULL) {
            throwInputChannelNotInitialized(env);
            return;
        }
    
        sp<InputWindowHandle> inputWindowHandle =
                android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
    
        status_t status = im->registerInputChannel(
                env, inputChannel, inputWindowHandle, monitor);
        if (status) {
            String8 message;
            message.appendFormat("Failed to register input channel.  status=%d", status);
            jniThrowRuntimeException(env, message.string());
            return;
        }
    
        if (! monitor) {
            android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
                    handleInputChannelDisposed, im);
        }
    }
    
    • NativeInputManagerregisterInputChannel还会调用到InputDispatcherregisterInputChannel
    • 通过InputChannel创建相应的Connection,同时将InputChannel加入到相应的监控之中
    • 在上面对代码的分析之中,获取InputChannel,就是通过这个Connection来获取的。
    status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
            const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
        { // acquire lock
            AutoMutex _l(mLock);
    
            if (getConnectionIndexLocked(inputChannel) >= 0) {
                return BAD_VALUE;
            }
    
            sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
    
            int fd = inputChannel->getFd();
            mConnectionsByFd.add(fd, connection);
    
            if (monitor) {
                mMonitoringChannels.push(inputChannel);
            }
    
            mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
        } // release lock
    
        // Wake the looper because some connections have changed.
        mLooper->wake();
        return OK;
    }
    
    • 经过InputManager的层层传递,最终会到达InputDispatcher之中,然后对其进行封装,并在其内部进行保存,同时也传递了相应的窗口的句柄,方便了后期在事件传递的时候,对于窗口的判断。

    参考

    Android系统源码剖析-事件分发
    Input系统—事件处理全过程

    相关文章

      网友评论

        本文标题:Android触摸事件的传递(五)--输入系统-InputCha

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