当前位置: 首页 > news >正文

北京网站手机站建设公司电话号码搜索引擎有哪些分类

北京网站手机站建设公司电话号码,搜索引擎有哪些分类,网站便捷营销,全国最好设计培训Input事件在应用中的传递(一) hongxi.zhu 2023-4-25 前面我们已经梳理了input事件在native层的传递,这一篇我们接着探索input事件在应用中的传递与处理,我们将按键事件和触摸事件分开梳理,这一篇就只涉及按键事件。 一、事件的接收 从前面的…

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. events=0x%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.  events=0x%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) {...ScopedLocalRef<jobject> 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,并将输入事件信息装入msg(InputMessage)} 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 继承于InputEvent)if (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_cast<KeyEvent*>(inputEvent));  //将consume()中拿到的inputEvent转为相应的KeyEvent(在内部我们创建的就是KeyEvent)break;...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");List<InputEvent> 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中会通过QueuedInputEventenqueueInputEvent将事件加入队列中再处理

//frameworks/base/core/java/android/view/ViewRootImpl.java@UnsupportedAppUsagevoid 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方法中的内容,我们得出整个链条的结构

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.java@Overridepublic 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.java@Overridepublic 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.java@Overridepublic 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;}
http://www.ds6.com.cn/news/54378.html

相关文章:

  • wordpress 文章样式百度seo优化软件
  • 移动局域网ip做网站怎样加入网络营销公司
  • 用dw6做网站什么是电商平台推广
  • 网站域名备案和做网站重庆seo网站系统
  • 宝鸡微网站建设seo营销怎么做
  • 办公家具 技术支持 东莞网站建设2023年广州疫情最新消息
  • 网站建设培训基地深圳高端网站建设公司
  • vps可以多少wordpress潍坊网站建设优化
  • 深圳企业网站开发百度竞价排名费用
  • 跟做网站相关的法律什么是百度搜索推广
  • 美国建网站的价格一键建站免费
  • 知名外贸网站建设公司宁夏百度推广代理商
  • wordpress教程安装教程视频百度seo通科
  • 公司建了网站怎么做分录免费刷赞网站推广qq免费
  • 微信小程序开发工具手机版seo点击排名源码
  • 桥头镇网站仿做seo培训资料
  • wordpress别人访问时不能正常显示南宁网站优化公司电话
  • 网站制作什么语言最好java培训学费多少钱
  • 网站做产品的审核吗武汉网站seo服务
  • 网站开发这行怎么样推广引流渠道平台
  • 织梦dedecms电影网站模板站长推荐产品
  • phpweb手机网站职业培训机构有哪些
  • 美女色情做视频网站有哪些在线的crm系统软件
  • wordpress设置非管理员百度seo搜索排名
  • 残联网站建设最近新闻热点
  • 网站开发工程师岗位今日新闻头条新闻摘抄
  • 做独立购物网站跨境网站建站
  • 韩国吃秀在哪个网站做直播seo收费
  • 简单做任务赚钱网站seo优化运营
  • 成都哪些公司可以做网站免费网站软件