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

  • [ANR] Input ANR是怎么产生的
  • [Android输入体系] socket衔接的创立

今天首要讲讲App端在收到事情之后,是怎么消费这些事情的。

首先,咱们看一个事情分发的典型Java堆栈

[Android输入系统] App端消费事件流程

能够看到,事情是从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