android发现之旅之输入事件如何在应用端传递

根据 http://opendevkit.com/?e=79 , 我们了解到, InputDispatcher最后会调用dispatchKeyLocked分发key.之前说过,InputDispatcher分发事件,会先给系统过滤一下,就是调用的notifyKey -> mPolicy->interceptKeyBeforeQueueing, 我们这里不讨论这种情况. notifyKey最后,调用了mLooper->wake();唤醒了InputDispatcherThread::threadLoop,也就是

mDispatcher->dispatchOnce(); -> dispatchOnceInnerLocked -> dispatchKeyLocked, 我们来分析这个流程dispatchKeyLocked.

1. InputDispatcher (事件分发端)

dispatchKeyLocked这个接口里:

先找到合适的target,实际合适的target通过findFocusedWindowTargetsLocked接口来找,字面意思是说找到焦点所在的window.

774     // Identify targets.

   775     Vector<InputTarget> inputTargets;                                                                                                    

   776     int32_t injectionResult =

findFocusedWindowTargetsLocked(currentTime,

   777             entry, inputTargets, nextWakeupTime);

   778     if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {

   779         return false;

   780     }

   781

   782     setInjectionResultLocked(entry, injectionResult);

   783     if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {

   784         return true;

   785     }

   786

   787     addMonitoringTargetsLocked(inputTargets);

   788

找到之后, 调用dispatchEventLocked继续分发.

   789     // Dispatch the key.

   790     dispatchEventLocked(currentTime, entry, inputTargets);

dispatchEventLocked -> prepareDispatchCycleLocked -> enqueueDispatchEntriesLocked -> connection->inputPublisher.publishKeyEvent -> mChannel->sendMessage -> ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); 实际最后调用的socket发出数据了.

(inputPublisher.publishKeyEvent -> mChannel->sendMessage -> ::send在InputTransport.cpp里的定义)

inputPublisher在frameworks/base/libs/androidfw/InputTransport.cpp定义, 可以想像InputTransport.cpp是事件分发端和事件接受端都要调用的.

这样, 事件分发端, 最后用socket接口发出了数据.下面分析客户端.

2. 事件接受端之: InputTransport.cpp

文件里, 我们看到InputChannel::receiveMessage接口, 调用了::recv从socket读取数据.可以想象, 一定是这个接口被调用到的.怎么被调用的呢?我们看到InputConsumer::consume接口调用了InputChannel::receiveMessage.

可以想象: InputChannel是事件分发端InputDispatcher和事件消费端InputConsumer都使用的.那一定有地方使用了InputConsumer和InputChannel.

3. 正向分析

ViewRootImpl的setView接口是activtity初始化过程中,必须要调用的.setView接口里有几个关键的地方:

(1)InputChannel初始化和使用

mInputChannel = new InputChannel();

...

res = mWindowSession.

addToDisplay

(mWindow, mSeq, mWindowAttributes,

   getHostVisibility(), mDisplay.getDisplayId(),

   mAttachInfo.mContentInsets,

mInputChannel

);

addToDisplay (Session.java) -> mService.addWindow (WindowManagerService.java):

2215                 String name = win.makeInputChannelName();

   2216                 InputChannel[] inputChannels =

InputChannel.openInputChannelPair(name);这句创建了socket对,客户端一个服务器一个,也就是InputDispatcher一个, InputConsumer一个.

   2217                 win.setInputChannel(inputChannels[0]);

   2218                 inputChannels[1].transferTo(outInputChannel);

   2219

   2220                  mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);这句最后通过InputDispatcher关联了inputcha nnel,也就是InputDispatcher最后发送的就是对应的inputchannel

.

(2) InputConsumer的初始化和事件处理

还是ViewRootImpl::setView接口:

612   mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,                                              Looper.myLooper());

传递的2个参数,都很关键.mInputChannel, Looper.myLooper().接下来看WindowInputEventReceiver, 继承InputEventReceiver, 实现了一些回调, 直接看InputEventReceiver:

InputEventReceiver构造:

66         mInputChannel = inputChannel; 保存channel.

   67         mMessageQueue = looper.getQueue(); 获取消息队列

   68         mReceiverPtr = nativeInit(this, inputChannel, mMessageQueue);调用nativeInit, 在android_view_InputEventReceiver.cpp里实现:

构造了一个 new NativeInputEventReceiver, 传递的是:

inputChannel, messageQueue.先看NativeInputEventReceiver构造:

 mInputConsumer(inputChannel)

mMessageQueue(messageQueue),

这样, mInputConsumer实际上就是inputchannel.

构造了完 NativeInputEventReceiver调用了NativeInputEventReceiver的initialize接口, 这个接口:

94     mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL); 这句最关键了, 把channel的fd, 加到了looper的fd集合里, 并且传递了this进入, 本身47 class NativeInputEventReceiver : public LooperCallback {就是LooperCallback, 所以inputchannel的fd动作(InputDispatcher的send动作), 都会被looper回调到 NativeInputEventReceiver的接口里, 实际上就是:

NativeInputEventReceiver::handleEvent, 进而NativeInputEventReceiver::consumeEvents -> mInputConsumer.consume, 我们知道mInputConsumer.consume就是从socket的fd读东西了.

到此, InputDispatcher通过socket写的事件, 被NativeInputEventReceiver通过Inputcomsumer读出来了.

再向上就是向上回调了.

4. ViewRootImpl回调

NativeInputEventReceiver会回调WindowInputEventReceiver(ViewRootImpl.java), 继续调用 enqueueInputEvent -> doProcessInputEvents -> deliverInputEvent -> deliverKeyEvent

(1) 首先考虑ime, imm.dispatchKeyEvent, 实际上ime分发完了后, 会有回调的.handleImeFinishedEvent.

(2) 没有ime, 继续分发deliverKeyEventPostIme

deliverKeyEventPostIme:  3696         // Make sure the fallback event policy sees all keys that will be delivered to the

  3697         // view hierarchy.

  3698         mFallbackEventHandler.preDispatchKeyEvent(event); 实际上也干了不少事情, 比如对audiomanager的调用等.

  3699

  3700         // Deliver the key to the view hierarchy.

  3701         if (mView.dispatchKeyEvent(event)) { 在view tree上分发

  3702             finishInputEvent(q, true);

  3703             return;

  3704         }

  3705

  3706         // If the Control modifier is held, try to interpret the key as a shortcut.

  3707         if (event.getAction() == KeyEvent.ACTION_DOWN

  3708                 && event.isCtrlPressed()

  3709                 && event.getRepeatCount() == 0

  3710                 && !KeyEvent.isModifierKey(event.getKeyCode())) {

  3711             if (mView.dispatchKeyShortcutEvent(event)) {

  3712                 finishInputEvent(q, true);

  3713                 return;

  3714             }

  3715         }

  3716

  3717         // Apply the fallback event policy.

  3718         if (mFallbackEventHandler.dispatchKeyEvent(event)) {

  3719             finishInputEvent(q, true);

  3720             return;

  3721         }

  3722

按键分发到此结束.


我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章