Android(安卓)Input事件APP端流程分析
16lz
2021-12-04
先上流程图
WindowInputEventReceiverWindowInputEventReceiver为InputEventReceiver的子类是接收InputManagerService派发事件的APP端,在InputEventReceiver.cpp 中可以看到 接收到input事件后回调Java层的dispatchInputEvent 即 InputEventReceiver的dispatchInputEvent 方法,dispatchInputEvent 调用onInputEvent方法,onInputEvent进行重写,即调用WindowInputEventReceiver的onInputEvent
InputEventReceiver.cppstatus_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {//........ if (inputEventObj) { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName()); }//回调Java层dispatchInputEvent env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj); //............. }}InputEventReceiver.java private void dispatchInputEvent(int seq, InputEvent event, int displayId) { mSeqMap.put(event.getSequenceNumber(), seq); onInputEvent(event, displayId); }
WindowInputEventReceiver的onInputEvent调用
ViewRootImpl的enqueueInputEvent -->doProcessInputEvents-->deliverInputEvent
在deliverInputEvent方法内进行 InputStage的链式调用如下
//WindowInputEventReceiver 继承于 InputEventReceiverfinal class WindowInputEventReceiver extends InputEventReceiver { public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); } @Override public void onInputEvent(InputEvent event, int displayId) { enqueueInputEvent(event, this, 0, true); }} void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) {.... doProcessInputEvents();.... }void doProcessInputEvents() {....deliverInputEvent(q);.... }private void deliverInputEvent(QueuedInputEvent q) { InputStage stage; if (q.shouldSendToSynthesizer()) { stage = mSyntheticInputStage; } else { stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; } if (q.mEvent instanceof KeyEvent) { mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent); } if (stage != null) { handleWindowFocusChanged(); //InputStage 进行链式调用//层层过滤,如果不处理交则给下一个(FORWARD),处理则返回FINISH_HANDLEstage.deliver(q); } else { finishInputEvent(q); }}
InputStage
InputStage | 说明 |
NativePreImeInputStage | 分发早于IME的InputEvent到NativeActivity中去处理, NativeActivity和普通acitivty的功能一致,不过是在native层实现,这样执行效率会更高,同时NativeActivity在游戏开发中很实用(不支持触摸事件)。 |
ViewPreIMEInputStage | 分发早于IME的InputEvent到View框架处理,会调用view(输入焦点)的onkeyPreIme方法,同时会给View在输入法处理key事件之前先得到消息并优先处理,View系列控件可以直接复写onKeyPreIme( 不支持触摸事件)。 |
ImeInputStage | 分发InputEvent到IME处理调用ImeInputStage的onProcess,InputMethodManager的dispatchInputEvent方法处理消息(不支持触摸事件)。 |
EarlyPostImeInputStage | 与touchmode相关,比如你的手机有方向键,按方向键会退出touchmode,这个事件被消费,有可能会有view的背景变化,但不确定(支持触摸事件)。 |
NativePostImeInputStage | 分发InputEvent事件到NativeActivity,IME处理完消息后能先于普通Activity处理消息(此时支持触摸事件)。 |
ViewPostImeInputStage | 分发InputEvent事件到View框架,view的事件分发(支持触摸事件)。最终会调用到输入焦点的3个方法:使用setKeyListener注册的监听器的onKey,之后是onKeyDown和onKeyUp,或者调用activity的onKeyDown和onKeyUp方法,也就是兜底处理无人处理的key事件 |
SyntheticInputStage | 未处理 InputEvent最后处理。 |
ViewPostImeInputStage是将InputEvent分发到View层的实现所在 onProcess 方法如下
final class ViewPostImeInputStage extends InputStage { ....... @Override protected 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 processPointerEvent(QueuedInputEvent q) { //..... //调用View层分发 这里的mView 为 boolean handled = mView.dispatchPointerEvent(event); ..... return handled ? FINISH_HANDLED : FORWARD; }
processPointerEvent 调用了mView层的dispatchPointerEvent分发, 这里的mView 为DecorView
DecorView 的dispatch方法会先调用Window.CallBack 或者KeyEvent.CallBack在进行View层的分发,
这里的Widow.CallBack的实现有Activity, Dialog等 先回调 Window.CallBack如果 返回false不处理则进行View层分发
DecorView.java @Override public boolean dispatchTouchEvent(MotionEvent ev) { final Window.Callback cb = mWindow.getCallback(); return cb != null && !mWindow.isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev); }
此后就是我们熟悉的View或ViewGroup的dispatchTouchEvent相关流程了
例如 ViewGroup判断 point的X,Y在那个子控件内,命中子控件后,再调用子控件的dispatchTouchEvent
如果命中的子控件不处理(比如setClickable(false)),或没有命中 则调用自身的dispatchTouchEvent
更多相关文章
- Android之Service学习篇一:Service启动方式之startService
- React Native与Android通信——Android(安卓)calls JS(一)0.45
- Android(安卓)中文 API (21) —— DigitalClock
- Android中listview中的button
- Android(安卓)报错处理:Android(安卓)resource linking failed
- Android中字体的处理
- android noTouch 事件
- Android(安卓)Application Fundamentals——Android应用程序基础
- Android监听消息通知栏点击事件