Key event 分发流程研究心得

Android在分发Input event(Key, Motion, Pointer,TrackBall)的流程牵扯到的WindowMangerService,ViewRootImpl和InputDispatcherThread三位角色.这三位角色的工作关系会依以下三个阶段来分析.

        1. 起源.

    2. 指定接受者.

    3. 处理输入事件流程.

起源

     在之前的输入法研究分析可以知道, 在android一开机的时候会去启动InputManager service, 此service会再去启动InputReaderThread和InputDispatcherThread,前者是从driver哪边获取inputevent放入Queue中在通知InputDispatcherThread去Queue中捞出来放进linux中的共享内存, 之后在通知ViewRootImpl去作分发的处理. 这一篇着重在于分发的过程. 至于inputevent在android中的流程机制之前就有作过分析了. 这里就不再重复描述. 就从InputDispatcherThread被启动的流程开始分析.

threadLoop @InputDispatcher.cpp
bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}
 
threadLoop @InputDispatcher.cpp
void InputDispatcher::dispatchOnce() {
    // do something
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime,            
                                          nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);         1
}

1. 开始针对目前process的looper作polling状态. 检查是否有新的inputevent

pollOnce @Looper.cpp
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    for (;;) {
    // do something
       result = pollInner(timeoutMillis);
    }
   
}
 
pollInner@ Looper.cpp
int Looper::pollInner(int timeoutMillis) {
    //do something
 
    int result = ALOOPER_POLL_WAKE;
    mResponses.clear();
    mResponseIndex = 0;
 
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd, eventItems,
                           EPOLL_MAX_EVENTS, timeoutMillis);   1
 
    //处理所监听到的相关事件
 
    //do something
   
    sp<MessageHandler> handler = messageEnvelope.handler;
    Message message = messageEnvelope.message;
    mMessageEnvelopes.removeAt(0);
    mSendingMessage = true;
    mLock.unlock();
    handler->handleMessage(message);                               2
 
    //do something
    if (response.request.ident == ALOOPER_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);       3
    if (callbackResult == 0) {
                removeFd(fd);
     }
            response.request.callback.clear();
            result = ALOOPER_POLL_CALLBACK;
 
}

1. 利用epoll_wait函数来监听目前looper上的event,一有event其eventCount就会回传event的数目. 假若在最后一个参数指定的 timeoutMillis内都没有事件发生, 其eventCount就会回传0,若有发生错误回传-1.由 EPOLL_MAX_EVENTS 的定义值可以知道, 所监听到的event数最多为16个event.

 

2. 呼叫执行所有在looper中的闲置中的Message所带的CallBackfunction handleMessage

 

3. 呼叫执行所有在looper中的reponse eventCallBack function handleEvent

由上面的程序代码分析可以知道在一开始InputDispatcherThread就会一直开始监听目前looper上是否有message或是event需要处理.

指定接受者.

        当Android app一开始要去绘制整个layout时,WindowsManager会先作 addView 的处理还将自己的View先设定到目前的Window上. 就从WindowManager的addViewfunction开始分析.

addView@ WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
      //do something
      root.setView(view, wparams, panelParentView);
      //do something
}
 
setView@ ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
      //do something
      mInputEventReceiver = new
                          WindowInputEventReceiver(mInputChannel,
                                Looper.myLooper());              1
     //do something
 
}
 
WindowInputEventReceiver@ ViewRootImpl.java
final class WindowInputEventReceiver extends InputEventReceiver {
        public WindowInputEventReceiver(InputChannel inputChannel, Looper
                                    looper) {
            super(inputChannel, looper);                           2
        }
 
        @Override
        public void onInputEvent(InputEvent event) {
            enqueueInputEvent(event, this, 0, true);
        }
 
        @Override
        public void onBatchedInputEventPending() {
            scheduleConsumeBatchedInput();
        }
 
        @Override
        public void dispose() {
            unscheduleConsumeBatchedInput();
            super.dispose();
        }
}
1. 产生一个带有两个CallBack function实作的 WindowInputEventReceiver物件 . 这两个CallBack function onInputEvent onBatchedInputEventPending 会跟在nativelayer的 InputDispatcherThread 监听事件处理有相关.

 

2. 跟目前所在的Looper注册request CallBackobject NativeInputEventReceiver

处理输入事件流程.

        由前面起源的分析可以知道, InputDispatcherThread一直处在监听looper上的事件阶段, 一旦监听到looper上有event就会接者呼叫执行event所带的CallBackfunctionhandleEvent . 至于是呼叫哪一个event所带的handleEvent函数. 这就要由程序代码 response.request.callback->handleEvent 开始分析. 就由前面指定接受者的第二个步骤开始分析.

WindowInputEventReceiver@ ViewRootImpl.java
public WindowInputEventReceiver(InputChannel inputChannel, Looper
                                    looper) {
            super(inputChannel, looper);                          
}
 
InputEventReceiver@ InputEventReceiver.java
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
     //do something
     mInputChannel = inputChannel;
     mMessageQueue = looper.getQueue();
     mReceiverPtr = nativeInit(this, inputChannel, mMessageQueue);
     //do somthing
}
 
nativeInit@ android_view_InputEventReceiver.cpp
static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
        jobject inputChannelObj, jobject messageQueueObj) {
    //do something
    status_t status = receiver->initialize();
    //do something
}
 
NativeInputEventReceiver::initialize@ android_view_InputEventReceiver.cpp
status_t NativeInputEventReceiver::initialize() {
    int receiveFd = mInputConsumer.getChannel()->getFd();
    mMessageQueue->getLooper()->addFd(receiveFd, 0,
                       ALOOPER_EVENT_INPUT, this, NULL);
    return OK;
}
 
Looper::addFd@Looper.cpp
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>&
                    callback, void* data) {
 
    //do something
    Request request;
    request.fd = fd;
    request.ident = ident;
    request.callback = callback;
    request.data = data;
   
    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event));
    eventItem.events = epollEvents;
    eventItem.data.fd = fd;
 
    ssize_t requestIndex = mRequests.indexOfKey(fd);
    if (requestIndex < 0) {
            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);
        } else {
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, &
                          eventItem);
            if (epollResult < 0) {
                ALOGE("Error modifying epoll events for fd %d, errno=%d",
                       fd, errno);
                return -1;
            }
            mRequests.replaceValueAt(requestIndex, request);
        }
 
    //do something
}
由上面的程序代码流程可以知道, 在一开始初始化WindowInputEventReceiver对象时, 又去呼叫父类别InputEventReceiver建构子. 这时就又去呼叫JNI函数nativeInit去做初始化流程, 此初始化的流程就正式注册Looper上的CallBackevent 所带的信息. 也就是说先把request的information记录下来, 等到要回reponse的时候就知道要回给哪一个request.由此可知, 此程序代码 response.request.callback->handleEvent 等同于

(NativeInputEventReceiver*)receiver-> handleEvent.

NativeInputEventReceiver::handleEvent@ android_view_InputEventReceiver.cpp
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    //do something
    status_t status = consumeEvents(env, false /*consumeBatches*/, -1);
    //do something
    return status == OK || status == NO_MEMORY ? 1 : 0;
}
 
NativeInputEventReceiver::consumeEvents@ android_view_InputEventReceiver.cpp
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime) {
     //do something
     for (;;) {
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent);
        //do something
        switch (inputEvent->getType()) {
            case AINPUT_EVENT_TYPE_KEY:
                inputEventObj = android_view_KeyEvent_fromNative(env,
                        static_cast<KeyEvent*>(inputEvent));
                break;
 
            case AINPUT_EVENT_TYPE_MOTION:
                inputEventObj =
                        android_view_MotionEvent_obtainAsCopy(env,
                               static_cast<MotionEvent*>(inputEvent));
                break;
 
            default:
                assert(false);
                inputEventObj = NULL;
            }
 
        env->CallVoidMethod(mReceiverObjGlobal,
                       gInputEventReceiverClassInfo.dispatchInputEvent,
                       seq, inputEventObj);
        //do something
     }
}
 
dispatchInputEvent@ InputEventReceiver.java
// Called from native code.
    @SuppressWarnings("unused")
    private void dispatchInputEvent(int seq, InputEvent event) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event);
    }
这一路下来, 由native layer的 handleEventfunction 藉由 InputConsumer对象将新的input event取出来, 然后就传给javalayer的 dispatchInputEvent 函数继续作分发的处理. 这里值得一提的是我们发现dispatchInputEvent函数会继续将event传给 onInputEvent函数, 然而此函数所属的InputEventReceiver是一个抽像类别, 抽象类是无法实体化的, 所以此函数最有可能实作在抽像类别所衍生的类别里. InputEventReceiver类别衍生的类别是 WindowInputEventReceiver类别, 所以就继续从WindowInputEventReceiver的 onInputEvent函数分析

onInputEvent@ ViewRootImpl.java
@Override
        public void onInputEvent(InputEvent event) {
            enqueueInputEvent(event, this, 0, true);
        }
 
enqueueInputEvent @ ViewRootImpl.java
void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
     //do something
 
    if (processImmediately) {
        doProcessInputEvents();
    } else {
        scheduleProcessInputEvents();
    }
}
 
doProcessInputEvents@ ViewRootImpl.java
void doProcessInputEvents() {
        while (mCurrentInputEvent == null && mFirstPendingInputEvent != null)
        {
            QueuedInputEvent q = mFirstPendingInputEvent;
            mFirstPendingInputEvent = q.mNext;
            q.mNext = null;
            mCurrentInputEvent = q;
            deliverInputEvent(q);
        }
 
        // We are done processing all input events that we can process right now
        // so we can clear the pending flag immediately.
        if (mProcessInputEventsScheduled) {
            mProcessInputEventsScheduled = false;
            mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
        }
    }
 
deliverInputEvent @ ViewRootImpl.java
private void deliverInputEvent(QueuedInputEvent q) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
        try {
            if (q.mEvent instanceof KeyEvent) {
                deliverKeyEvent(q);
            } else {
                final int source = q.mEvent.getSource();
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0)
                {
                    deliverPointerEvent(q);
                } else if ((source &
                    InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                    deliverTrackballEvent(q);
                } else {
                    deliverGenericMotionEvent(q);
                }
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
 
deliverKeyEvent @ ViewRootImpl.java
private void deliverKeyEvent(QueuedInputEvent q) {
     //do something
     deliverKeyEventPostIme(q);
}
 
deliverKeyEventPostIme @ ViewRootImpl.java
private void deliverKeyEventPostIme(QueuedInputEvent q) {
    //do something
    mView.dispatchKeyEvent(event)
    //do something
}
 
dispatchKeyEvent @View.java
public boolean dispatchKeyEvent(KeyEvent event) {
    //do something
    ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnKeyListener != null && (mViewFlags &  
                            ENABLED_MASK) == ENABLED
                    && li.mOnKeyListener.onKey(this,
                        event.getKeyCode(), event)) {              1
            return true;
     }
   
    if (event.dispatch(this, mAttachInfo != null
                ? mAttachInfo.mKeyDispatchState : null, this)) {     2
            return true;
        }
    //do something
    
}
1. 自定义的view若有注册Key Listener,就会由此接收到

Key event.

 

2. 一般的Key event是由此函数 dispatch来作分发.

 

dispatch @ KeyEvent.java
public final boolean dispatch(Callback receiver, DispatcherState state,
            Object target) {
 
  //do something
  switch (mAction) {
  case ACTION_DOWN:
     //do something
     receiver.onKeyDown(mKeyCode, this);
  case ACTION_UP:
     //do something
     receiver.onKeyUp(mKeyCode, this);
  case ACTION_MULTIPLE:
     //do something
     receiver.onKeyMultiple(code, count, this)
  }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值