Activity注册InputChannel和处理Event(六)

5Activity注册InputChannel和处理Event

5.1 注册InputChannel

InputChannel的涉及面比较广,我们知道,仅有android手机当前界面的activity能响应键盘/触摸等事件,那InputChannel肯定和界面刚准备显示的流程代码有关系,在创建界面时,会调用ViewRootImpl的setView方法,最后会调用WMS的addWindow方法。

在android 6.0 中, 在WallpaperService中的updateSurface方法中创建InputChannel对象然后调用Session的addToDisplay方法,

mInputChannel = new InputChannel();
 if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
         Display.DEFAULT_DISPLAY, mContentInsets, mStableInsets, mOutsets,
                                mInputChannel) < 0) {

最后也会调用WMS的addWindow方法,至于界面的绘制流程以后再详细的跟踪。

暂时就先从这儿看起,

if (outInputChannel != null && (attrs.inputFeatures
       & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                String name = win.makeInputChannelName();
                InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
                win.setInputChannel(inputChannels[0]);
                inputChannels[1].transferTo(outInputChannel);

                mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
            }

registerInputChannel方法上面已经分析过了,主要分析openInputChannelPair方法

static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
        jclass clazz, jstring nameObj) {
    const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
    String8 name(nameChars);
    env->ReleaseStringUTFChars(nameObj, nameChars);

    sp<InputChannel> serverChannel;
    sp<InputChannel> clientChannel;
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
•••
    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(serverChannel));
•••
    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(clientChannel));
  •••

    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    return channelPair;
}

首先调用openInputChannelPair方法创建2个InputChannel,一个客户端,一个服务端。然后创建2个对应的NativeInputChannel对象。

所以addWindow中的inputChannels变量中inputChannels[0] 代表服务端, inputChannels[1] 代表客户端。

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;
        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",
                name.string(), 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;
}

openInputChannelPair为2个InputChannel对象都创建了sockets。

服务端的InputChannel通过registerInputChannel方法注册到系统服务进程中,这样客户端和服务端的InputChannel就可以通过sockets通信了。


5.2 接收Event

4.1 节中有个问题是应用在哪儿接收Event呢.

在ViewRootImpl的setView方法中调用WMS的addWindow后,会新建

WindowInputEventReceiver对象,总体流程图如下:


if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }

Looper.myLooper是应用程序的主线程。

直接看Looper.cpp的addFd方法,将fd标志添加到epoll监控中,并且将指向NativeInputEventReceiver的request添加到mRequests列表中。

因此,应用程序可以收到socket发送的Event消息,并且可以回调NativeInputEventReceiver中的方法。

int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
            if (epollResult < 0) {
                ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
                return -1;
            }
            mRequests.add(fd, request);

详细的回调流程图如下:


消息循环会调用搭配looper对象的pollonce方法,

pollInner方法调用epoll_wait等待epoll事件的到来,事件到来后就调用NativeInputEventReceiver的handleEvent方法。

int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
•••
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);
            if (callbackResult == 0) {
                removeFd(fd, response.request.seq);
            }

 // Clear the callback reference in the response structure promptly because we
           // will not clear the response vector itself until the next poll.
            response.request.callback.clear();
            result = POLL_CALLBACK;
        }
•••

consumeEvents 首先会调用InputConsumer的consume方法来接收输入Event,

然后通过JNI调用InputEventReceiver的dispatchInputEvent等方法。

InputEvent* inputEvent;
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent);
•••
env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);

consume方法获取Event,看到了吧,刚好和4.1 小节中sendMessage方法相对应

status_t result = mChannel->receiveMessage(&mMsg);

具体实现如下,调用系统的recv方法。

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;
}

最后, ViewRootImpl的enqueueInputEvent方法如下,

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;
        Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                mPendingInputEventCount);

        if (processImmediately) {
            doProcessInputEvents();
        } else {
            scheduleProcessInputEvents();
        }
    }

Event 兜兜转转,转转兜兜,真艰难啊。

至此,Event终于从底层从系统走到应用中了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值