眉山建设银行官方网站,做外贸没网站可以吗,北京模板网站建设费用,郑州市网站Input事件在应用中的传递(一) hongxi.zhu 2023-4-25 前面我们已经梳理了input事件在native层的传递#xff0c;这一篇我们接着探索input事件在应用中的传递与处理#xff0c;我们将按键事件和触摸事件分开梳理#xff0c;这一篇就只涉及按键事件。
一、事件的接收
从前面的…Input事件在应用中的传递(一) hongxi.zhu 2023-4-25 前面我们已经梳理了input事件在native层的传递这一篇我们接着探索input事件在应用中的传递与处理我们将按键事件和触摸事件分开梳理这一篇就只涉及按键事件。
一、事件的接收
从前面的篇幅我们知道framework native层InputDispatcher向应用通过socket方式发送事件,应用的Looper 通过epoll方式监听sockcet的fd, 当应用的socket变为可读时例如它有可读事件Looper将回调handleEvent。 此时应用应读取已进入套接字的事件。 只要socket中有未读事件函数 handleEvent 就会继续触发。(这个event不是真正的输入事件只是Looper的状态event)
//frameworks/base/core/jni/android_view_InputEventReceiver.cppint NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {// Allowed return values of this function as documented in LooperCallback::handleEventconstexpr int REMOVE_CALLBACK 0;constexpr int KEEP_CALLBACK 1;//注意下面这个event不是真正的输入事件只是Looper的状态eventif (events (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {//当inputdispatcher异常导致socket被关闭或者目标窗口正在被移除或者传递窗口时输入法但是输入法正在关闭时会直接抛弃这个事件// This error typically occurs when the publisher has closed the input channel// as part of removing a window or finishing an IME session, in which case// the consumer will soon be disposed as well.if (kDebugDispatchCycle) {ALOGD(channel %s ~ Publisher closed input channel or an error occurred. events0x%x,getInputChannelName().c_str(), events);}return REMOVE_CALLBACK;}//如果是输入事件即是framework传递过来的事件时需要处理时if (events ALOOPER_EVENT_INPUT) {JNIEnv* env AndroidRuntime::getJNIEnv();status_t status consumeEvents(env, false /*consumeBatches*/, -1, nullptr);mMessageQueue-raiseAndClearException(env, handleReceiveCallback);return status OK || status NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK;}//如果已处理的事件需要告知inputdispatcher这个事件已处理时if (events ALOOPER_EVENT_OUTPUT) {const status_t status processOutboundEvents();if (status OK || status WOULD_BLOCK) {return KEEP_CALLBACK;} else {return REMOVE_CALLBACK;}}ALOGW(channel %s ~ Received spurious callback for unhandled poll event. events0x%x,getInputChannelName().c_str(), events);return KEEP_CALLBACK;
}handleEvent区分是本次Looper获取到的event, 是需要系统处理接收输入事件还是需要回复给InputDispatcher事件处理结束的event如果是需要处理的输入事件就调用consumeEvents消费这个事件。(注意这个event不是真正的输入事件只是Looper的状态case)
//frameworks/base/core/jni/android_view_InputEventReceiver.cppstatus_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {...ScopedLocalRefjobject receiverObj(env, nullptr);bool skipCallbacks false;for (;;) {uint32_t seq;InputEvent* inputEvent;//真正的去获取socket发过来的事件并构建成具体的某种InputEvent例如KeyEventstatus_t status mInputConsumer.consume(mInputEventFactory,consumeBatches, frameTime, seq, inputEvent);if (status ! OK status ! WOULD_BLOCK) {ALOGE(channel %s ~ Failed to consume input event. status%s(%d),getInputChannelName().c_str(), statusToString(status).c_str(), status);return status;}...consumeEvents中我们才开始真正的拿着对应的socket fd去读取socket的msg, 具体读取会调用InputConsumer::consume
//frameworks/native/libs/input/InputTransport.cppstatus_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {...*outSeq 0;*outEvent nullptr;// Fetch the next input message.// Loop until an event can be returned or no additional events are received.while (!*outEvent) { //获取到一次真正的事件就退出if (mMsgDeferred) {...} else {// Receive a fresh message.status_t result mChannel-receiveMessage(mMsg); //通过InputChannel来接收socket中真正的InputMessage(描述事件的结构体)...}...}}return OK;
}InputConsumer::consume中获取事件实际上是通过InputChannel去读取
frameworks/native/libs/input/InputTransport.cppstatus_t InputChannel::receiveMessage(InputMessage* msg) {ssize_t nRead;do {nRead ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT); //在这里真正的读取socket fd,并将输入事件信息装入msgInputMessage)} while (nRead -1 errno EINTR);...return OK; //最后返回OK
}通过InputChannel去读取真正的事件信息并装入InputMessage对象最后返回OK
//frameworks/native/libs/input/InputTransport.cppstatus_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {...*outSeq 0;*outEvent nullptr;// Fetch the next input message.// Loop until an event can be returned or no additional events are received.while (!*outEvent) { //获取到一次真正的事件就退出if (mMsgDeferred) {// mMsg contains a valid input message from the previous call to consume// that has not yet been processed.mMsgDeferred false;} else {// Receive a fresh message.status_t result mChannel-receiveMessage(mMsg); //通过InputChannel来接收socket中真正的InputMessage(描述事件的结构体)if (result OK) {mConsumeTimes.emplace(mMsg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC));}if (result) { //result OK 0 所以并不会进入批处理流程// Consume the next batched event unless batches are being held for later.if (consumeBatches || result ! WOULD_BLOCK) {result consumeBatch(factory, frameTime, outSeq, outEvent);if (*outEvent) {if (DEBUG_TRANSPORT_ACTIONS) {ALOGD(channel %s consumer ~ consumed batch event, seq%u,mChannel-getName().c_str(), *outSeq);}break;}}return result;}}switch (mMsg.header.type) {case InputMessage::Type::KEY: {KeyEvent* keyEvent factory-createKeyEvent(); //创建KeyEventif (!keyEvent) return NO_MEMORY;initializeKeyEvent(keyEvent, mMsg); //将InputMessage信息填充到keyEvent*outSeq mMsg.header.seq;*outEvent keyEvent; // 返回到上一级使用keyEvent 继承于InputEventif (DEBUG_TRANSPORT_ACTIONS) {ALOGD(channel %s consumer ~ consumed key event, seq%u,mChannel-getName().c_str(), *outSeq);}break;}...}}return OK;
}回到上一级InputConsumer::consume中,receiveMessage中获取到的InputMessage在这里转化为对应的event类型对应的按键事件就是KeyEvent*outEvent keyEvent返回到前面的NativeInputEventReceiver::consumeEvents中
//frameworks/base/core/jni/android_view_InputEventReceiver.cppstatus_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {...for (;;) {uint32_t seq;InputEvent* inputEvent;//真正的去获取socket发过来的事件并构建成具体的某种InputEvent例如KeyEventstatus_t status mInputConsumer.consume(mInputEventFactory,consumeBatches, frameTime, seq, inputEvent);...if (!skipCallbacks) {jobject inputEventObj;switch (inputEvent-getType()) {case AINPUT_EVENT_TYPE_KEY:if (kDebugDispatchCycle) {ALOGD(channel %s ~ Received key event., getInputChannelName().c_str());}inputEventObj android_view_KeyEvent_fromNative(env,static_castKeyEvent*(inputEvent)); //将consume()中拿到的inputEvent转为相应的KeyEvent在内部我们创建的就是KeyEventbreak;...if (inputEventObj) {...env-CallVoidMethod(receiverObj.get(),gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj); //jni调用InputEventReceiver.java中的InputEventReceiver将事件传递到java的世界...env-DeleteLocalRef(inputEventObj);}...}}
}通过consume中拿到inputEvent,然后转为KeyEvent最通过Jni的方式调用java中的InputEventReceiver的方法dispatchInputEvent正式开始事件的分发。
//frameworks/base/core/java/android/view/InputEventReceiver.javapublic abstract class InputEventReceiver {...// Called from native code.SuppressWarnings(unused)UnsupportedAppUsage(maxTargetSdk Build.VERSION_CODES.R, trackingBug 170729553)private void dispatchInputEvent(int seq, InputEvent event) {mSeqMap.put(event.getSequenceNumber(), seq);onInputEvent(event);}...
}InputEventReceiver是一个抽象类但是对应的dispatchInputEvent方法它的子类WindowInputEventReceiver并没有实现所以native层调用父类的InputEventReceiver的方法这个方法中接着调用了onInputEvent接着处理。onInputEvent子类是有实现的所以会走子类的方法。
//frameworks/base/core/java/android/view/ViewRootImpl.java...final class WindowInputEventReceiver extends InputEventReceiver {public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {super(inputChannel, looper);}Overridepublic void onInputEvent(InputEvent event) {Trace.traceBegin(Trace.TRACE_TAG_VIEW, processInputEventForCompatibility);ListInputEvent processedEvents;try {//对M版本之前的触摸事件的兼容处理按键事件不涉及, return nullprocessedEvents mInputCompatProcessor.processInputEventForCompatibility(event); } finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}if (processedEvents ! null) {if (processedEvents.isEmpty()) {// InputEvent consumed by mInputCompatProcessorfinishInputEvent(event, true);} else {for (int i 0; i processedEvents.size(); i) {enqueueInputEvent(processedEvents.get(i), this,QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);}}} else { //因为上面返回null 所以走到这里//在这里将自己this传入//processImmediately 为true意味着需要马上处理而不是延迟处理 enqueueInputEvent(event, this, 0, true);}}...onInputEvent中会通过QueuedInputEvent的enqueueInputEvent将事件加入队列中再处理
//frameworks/base/core/java/android/view/ViewRootImpl.javaUnsupportedAppUsagevoid enqueueInputEvent(InputEvent event,InputEventReceiver receiver, int flags, boolean processImmediately) {QueuedInputEvent q obtainQueuedInputEvent(event, receiver, flags); //将事件加入队列确保事件的有序处理if (event instanceof MotionEvent) {...} else if (event instanceof KeyEvent) { //如果案件事件是一个key的canceled事件KeyEvent ke (KeyEvent) event;if (ke.isCanceled()) {EventLog.writeEvent(EventLogTags.VIEW_ENQUEUE_INPUT_EVENT, Key - Cancel,getTitle().toString());}}// 无论时间戳如何始终按顺序排列输入事件。// 我们这样做是因为应用本身或 IME 可能会注入事件// 我们希望确保注入的按键按照接收到的顺序进行处理不能仅仅通过时间戳的前后来确定顺序。// Always enqueue the input event in order, regardless of its time stamp.// We do this because the application or the IME may inject key events// in response to touch events and we want to ensure that the injected keys// are processed in the order they were received and we cannot trust that// the time stamp of injected events are monotonic.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(); //前面传进来的processImmediately true所以走这里处理} else {scheduleProcessInputEvents();}}为什么需要加入队列处理一般来说当InputDispatcher发送一个事件给应用应用需要处理完并反馈给InputDispatcher后者才会发送下一个事件本身操作流程就是串行的看起来是不需要队列来保证串行处理。但是不要忘记除了来自底层驱动的事件外应用和IME都是可以往应用进程注入事件的那么就需要保证处理的顺序。那么下一步就是从队头依次拿出事件来分发了对应方式是doProcessInputEvents
//frameworks/base/core/java/android/view/ViewRootImpl.javavoid doProcessInputEvents() {// Deliver all pending input events in the queue.while (mPendingInputEventHead ! null) {QueuedInputEvent q mPendingInputEventHead; //从队列中拿出数据分发确保有序mPendingInputEventHead q.mNext;if (mPendingInputEventHead null) {mPendingInputEventTail null;}q.mNext null;mPendingInputEventCount - 1;Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,mPendingInputEventCount);mViewFrameInfo.setInputEvent(mInputEventAssigner.processEvent(q.mEvent));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);}}前面将事件入队然后在doProcessInputEvents就开始从队头拿出并通过deliverInputEvent开始分发
//frameworks/base/core/java/android/view/ViewRootImpl.javaprivate void deliverInputEvent(QueuedInputEvent q) {Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, deliverInputEvent,q.mEvent.getId());...try {...InputStage stage;if (q.shouldSendToSynthesizer()) {stage mSyntheticInputStage;} else {//如果忽略输入法窗口则从mFirstPostImeInputStage阶段开始分发否则从mFirstInputStage开始stage q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;}...if (stage ! null) {handleWindowFocusChanged(); //在分发前确认是否焦点窗口变化了如果变化就需要更新焦点的信息stage.deliver(q); //调用对应的stage阶段的deliver方法分发事件} else {finishInputEvent(q);}} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}把事件从拿出下一步就是往view或者IME分发分发的过程这里会分为多个阶段(InputStage)来顺序执行, 这些阶段在ViewRootImpl中setView时会指定
//frameworks/base/core/java/android/view/ViewRootImpl.java/*** We have one child*/public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {synchronized (this) {if (mView null) {...// Set up the input pipeline.CharSequence counterSuffix attrs.getTitle();mSyntheticInputStage new SyntheticInputStage();InputStage viewPostImeStage new ViewPostImeInputStage(mSyntheticInputStage);InputStage nativePostImeStage new NativePostImeInputStage(viewPostImeStage,aq:native-post-ime: counterSuffix);InputStage earlyPostImeStage new EarlyPostImeInputStage(nativePostImeStage);InputStage imeStage new ImeInputStage(earlyPostImeStage,aq:ime: counterSuffix);InputStage viewPreImeStage new ViewPreImeInputStage(imeStage);InputStage nativePreImeStage new NativePreImeInputStage(viewPreImeStage,aq:native-pre-ime: counterSuffix);mFirstInputStage nativePreImeStage;mFirstPostImeInputStage earlyPostImeStage;mPendingInputEventQueueLengthCounterName aq:pending: counterSuffix;}}}InputStage这里采用责任链的设计模式从抽象类InputStage内容可以知道每一个子类都会将next指向下一个stage子类对象
//frameworks/base/core/java/android/view/ViewRootImpl.javaabstract class InputStage {private final InputStage mNext;protected static final int FORWARD 0;protected static final int FINISH_HANDLED 1;protected static final int FINISH_NOT_HANDLED 2;private String mTracePrefix;/*** Creates an input stage.* 将所有的阶段都组成一个链表next指向下一个阶段* param next The next stage to which events should be forwarded.*/public InputStage(InputStage next) {mNext next;}...从setView方法中的内容我们得出整个链条的结构 #mermaid-svg-bE3MNvWuZwJlwlFL {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-bE3MNvWuZwJlwlFL .error-icon{fill:#552222;}#mermaid-svg-bE3MNvWuZwJlwlFL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bE3MNvWuZwJlwlFL .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-bE3MNvWuZwJlwlFL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bE3MNvWuZwJlwlFL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bE3MNvWuZwJlwlFL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bE3MNvWuZwJlwlFL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bE3MNvWuZwJlwlFL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bE3MNvWuZwJlwlFL .marker.cross{stroke:#333333;}#mermaid-svg-bE3MNvWuZwJlwlFL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bE3MNvWuZwJlwlFL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bE3MNvWuZwJlwlFL .cluster-label text{fill:#333;}#mermaid-svg-bE3MNvWuZwJlwlFL .cluster-label span{color:#333;}#mermaid-svg-bE3MNvWuZwJlwlFL .label text,#mermaid-svg-bE3MNvWuZwJlwlFL span{fill:#333;color:#333;}#mermaid-svg-bE3MNvWuZwJlwlFL .node rect,#mermaid-svg-bE3MNvWuZwJlwlFL .node circle,#mermaid-svg-bE3MNvWuZwJlwlFL .node ellipse,#mermaid-svg-bE3MNvWuZwJlwlFL .node polygon,#mermaid-svg-bE3MNvWuZwJlwlFL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bE3MNvWuZwJlwlFL .node .label{text-align:center;}#mermaid-svg-bE3MNvWuZwJlwlFL .node.clickable{cursor:pointer;}#mermaid-svg-bE3MNvWuZwJlwlFL .arrowheadPath{fill:#333333;}#mermaid-svg-bE3MNvWuZwJlwlFL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bE3MNvWuZwJlwlFL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bE3MNvWuZwJlwlFL .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-bE3MNvWuZwJlwlFL .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-bE3MNvWuZwJlwlFL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bE3MNvWuZwJlwlFL .cluster text{fill:#333;}#mermaid-svg-bE3MNvWuZwJlwlFL .cluster span{color:#333;}#mermaid-svg-bE3MNvWuZwJlwlFL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-bE3MNvWuZwJlwlFL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} NativePreImeInputStage ViewPreImeInputStage ImeInputStage EarlyPostImeInputStage NativePostImeInputStage ViewPostImeInputStage SyntheticInputStage 分发阶段就会从第一个创建的stage子类开始执行到最后一个stage子类,无论要不要处理都要从链表的头传递到尾。 回到deliverInputEvent方法中stage.deliver(q)正式进入stage的分发中,观察下完整的一个stage的处理流程
//frameworks/base/core/java/android/view/ViewRootImpl.java/*** Delivers an event to be processed.*/public final void deliver(QueuedInputEvent q) {if ((q.mFlags QueuedInputEvent.FLAG_FINISHED) ! 0) { //如果上一stage中事件被处理FLAG_FINISHED那么本stage就不会再处理onProcess直接传递到下一个stage(无论是要处理链表都要走完)forward(q);} else if (shouldDropInputEvent(q)) {finish(q, false);} else {traceEvent(q, Trace.TRACE_TAG_VIEW);final int result;try {result onProcess(q); //如果前面的阶段没有被处理本stage就需要走处理流程} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}apply(q, result); //判断是否需要下一个阶段走处理流程}}/*** Marks the input event as finished then forwards it to the next stage.* 如果事件在当前阶段被结束q.mFlags被标记为FLAG_FINISHED并通过forward(q)传递给下一个阶段*/protected void finish(QueuedInputEvent q, boolean handled) {q.mFlags | QueuedInputEvent.FLAG_FINISHED;if (handled) {q.mFlags | QueuedInputEvent.FLAG_FINISHED_HANDLED;}forward(q);}/*** Forwards the event to the next stage.* 往下一个阶段分发*/protected void forward(QueuedInputEvent q) {onDeliverToNext(q);// 继续往下一个阶段传递}/*** Applies a result code from {link #onProcess} to the specified event.* 判断是否需要继续接着往下一个阶段分发*/protected void apply(QueuedInputEvent q, int result) {if (result FORWARD) { //如果上一个阶段还没处理这个事件则继续往下一个阶段分发处理forward(q);} else if (result FINISH_HANDLED) { //如果事件被处理了就标记为FLAG_FINISHED|FLAG_FINISHED_HANDLED然后继续传递给下一个阶段但不走onProcess()了finish(q, true);} else if (result FINISH_NOT_HANDLED) { //如果事件没有被处理则标记为FLAG_FINISHED然后继续传递给下一个阶段但不走onProcess()了finish(q, false);} else {throw new IllegalArgumentException(Invalid result: result);}}/*** Called when an event is ready to be processed.* return A result code indicating how the event was handled.*/protected int onProcess(QueuedInputEvent q) {return FORWARD;}/*** Called when an event is being delivered to the next stage.* 继续执行下一阶段的deliver*/protected void onDeliverToNext(QueuedInputEvent q) {if (DEBUG_INPUT_STAGES) {Log.v(mTag, Done with getClass().getSimpleName() . q);}if (mNext ! null) {mNext.deliver(q); //如果下一阶段不为空就继续执行下一阶段的deliver继续往下一阶段传递} else {finishInputEvent(q);}}具体如流程图 从NativePreImeInputStage开始deliver事件经过每一个stage, 如果该事件没有被处理标记为FLAG_FINISHED或者该事件应该被抛弃shouldDropInputEvent那么就应该传给本阶段stage处理onProcess按照这个逻辑一直跑完整个链表。
在这里阶段里我们本篇比较关心往View树分发的阶段即ViewPostImeInputStage
//frameworks/base/core/java/android/view/ViewRootImpl.java/*** Delivers post-ime input events to the view hierarchy.*/final class ViewPostImeInputStage extends InputStage {public ViewPostImeInputStage(InputStage next) {super(next);}// 子类重写了onProcess方法Overrideprotected int onProcess(QueuedInputEvent q) {if (q.mEvent instanceof KeyEvent) {return processKeyEvent(q); //如果是按键事件} else {final int source q.mEvent.getSource();if ((source InputDevice.SOURCE_CLASS_POINTER) ! 0) {return processPointerEvent(q);} else if ((source InputDevice.SOURCE_CLASS_TRACKBALL) ! 0) {return processTrackballEvent(q);} else {return processGenericMotionEvent(q);}}}...private int processKeyEvent(QueuedInputEvent q) {final KeyEvent event (KeyEvent)q.mEvent;if (mUnhandledKeyManager.preViewDispatch(event)) {return FINISH_HANDLED;}// 往view树分发事件// Deliver the key to the view hierarchy.if (mView.dispatchKeyEvent(event)) { // mView实际上是DecorView, 在addView时添加return FINISH_HANDLED;}...}ViewPostImeInputStage的处理在onProcess方法其中最关键的地方就是mView.dispatchKeyEvent(event)mView 实际上是传入的DecorView具体可以查看应用启动过程流程。通过DecorView的dispatchKeyEvent开始事件在View树的传递。
//frameworks/base/core/java/com/android/internal/policy/DecorView.javaOverridepublic boolean dispatchKeyEvent(KeyEvent event) {final int keyCode event.getKeyCode();final int action event.getAction();final boolean isDown action KeyEvent.ACTION_DOWN;//快捷键处理if (isDown (event.getRepeatCount() 0)) {// First handle chording of panel key: if a panel key is held// but not released, try to execute a shortcut in it.if ((mWindow.mPanelChordingKey 0) (mWindow.mPanelChordingKey ! keyCode)) {boolean handled dispatchKeyShortcutEvent(event);if (handled) {return true;}}// If a panel is open, perform a shortcut on it without the// chorded panel keyif ((mWindow.mPreparedPanel ! null) mWindow.mPreparedPanel.isOpen) {if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {return true;}}}//mWindow是PhoneWindow的实例if (!mWindow.isDestroyed()) {// 这个cb实际上是Activity对象(当调Activity的attach方法时 通过mWindow.setCallback(this)传入)final Window.Callback cb mWindow.getCallback();// 因为Activity这里不为null,所以会掉Activity的dispatchKeyEventfinal boolean handled cb ! null mFeatureId 0 ? cb.dispatchKeyEvent(event): super.dispatchKeyEvent(event);if (handled) {return true;}}return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event): mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);}这里关键的是这个cb对象根据应用的启动流程可知这个cb是当调Activity的attach方法时 通过mWindow.setCallback(this)传入的Activity对象且不为null,所以事件会传到Activity调用它的dispatchKeyEvent方法。
//frameworks/base/core/java/android/app/Activity.java/*** Called to process key events. You can override this to intercept all* key events before they are dispatched to the window. Be sure to call* this implementation for key events that should be handled normally.** param event The key event.** return boolean Return true if this event was consumed.*/public boolean dispatchKeyEvent(KeyEvent event) {onUserInteraction(); //通知通知栏进行相应的变化// Let action bars open menus in response to the menu key prioritized over// the window handling it// MENU键优先给ActionBar处理final int keyCode event.getKeyCode();if (keyCode KeyEvent.KEYCODE_MENU mActionBar ! null mActionBar.onMenuKeyEvent(event)) {return true;}// 这里的getWindow()拿到的是PhoneWindow对象(在Activity的attach方法中创建的)Window win getWindow();if (win.superDispatchKeyEvent(event)) { //这里会先分发给PhoneWindow, 实际上PhoneWindow会调DecorView的superDispatchKeyEventreturn true;}View decor mDecor;if (decor null) decor win.getDecorView();return event.dispatch(this, decor ! null? decor.getKeyDispatcherState() : null, this);}从这里可以看出Activity会调PhoneWindow的superDispatchKeyEvent将事件发给PhoneWindow处理
//frameworks/base/core/java/com/android/internal/policy/PhoneWindow.javaOverridepublic boolean superDispatchKeyEvent(KeyEvent event) {return mDecor.superDispatchKeyEvent(event); // 实际上调的是DecorView的方法让DecorView分发}//frameworks/base/core/java/com/android/internal/policy/DecorView.java/** hide */
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {...public boolean superDispatchKeyEvent(KeyEvent event) {...if (super.dispatchKeyEvent(event)) {return true;}return (getViewRootImpl() ! null) getViewRootImpl().dispatchUnhandledKeyEvent(event);}...
}实际上PhoneWindow会调DecorView的superDispatchKeyEvent最终又回到DecorView, 为什么这样流转呢仔细观察DecorView是继承于FrameLayout,而FrameLayout继承于ViewGroup那它就是树中最顶端的ViewGroup, 事件应该从它开始分发。
//frameworks/base/core/java/android/view/ViewGroup.javaOverridepublic boolean dispatchKeyEvent(KeyEvent event) {...if ((mPrivateFlags (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) //是否焦点是自己(ViewGroup) (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {if (super.dispatchKeyEvent(event)) { //调用父类的方法处理ViewGroup也是继承于Viewreturn true;}} else if (mFocused ! null (mFocused.mPrivateFlags PFLAG_HAS_BOUNDS) //是否焦点View是自己的子view或者子ViewGroup PFLAG_HAS_BOUNDS) {if (mFocused.dispatchKeyEvent(event)) { //传递给自己子view或者viewgroup处理return true;}}...return false;}这里会根据判断焦点view来决定分发给谁首先判断自己本身ViewGroup是不是焦点View,如果是则调用父类View的dispatchKeyEvent方法处理按键事件如果焦点view在自己的子view或者子viewgroup上则继续往下分发。如果是子viewgroup那么和上面流程一样继续判断是否继续往下如果是子view就调用view的dispatchKeyEvent处理所以最终都是View的dispatchKeyEvent处理。
// frameworks/base/core/java/android/view/View.java/*** Dispatch a key event to the next view on the focus path. This path runs* from the top of the view tree down to the currently focused view. If this* view has focus, it will dispatch to itself. Otherwise it will dispatch* the next node down the focus path. This method also fires any key* listeners.** param event The key event to be dispatched.* return True if the event was handled, false otherwise.*/public boolean dispatchKeyEvent(KeyEvent event) {...// Give any attached key listener a first crack at the event.//noinspection SimplifiableIfStatement// ListenerInfo是管理各种监听器的类它持有监听器的实例例如OnClickListener、OnTouchListenerListenerInfo li mListenerInfo;// 如果应用注册了监听器mOnKeyListener那么就优先调用mOnKeyListener.onKey回调if (li ! null li.mOnKeyListener ! null (mViewFlags ENABLED_MASK) ENABLED li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {return true;}//如果上述未处理则默认走KeyEvent的dispatch来处理按键事件if (event.dispatch(this, mAttachInfo ! null? mAttachInfo.mKeyDispatchState : null, this)) {return true;}...return false;}