IMS:Activity和View处理InputEvent
- 1、IMS服务处理
- 2、Activity的DecorView界面添加
- 3、Activity和View处理InputEvent
- 3.1 InputEventReceiver接收InputEvent
- 3.2 处理KeyEvent
- 3.3 处理MotionEvent
android12-release
1、IMS服务处理
关键流程
EventHub -> InputReader -> InputDispatcher -> InputChannel/android_view_InputEventReceiver.cpp -> InputEventReceiver.java
;查看如下相关文章和时序图
- InputReader线程获取输入事件-Android12
- InputDispatcher线程分发事件-Android12
- InputChannel通道建立-Android12
- InputChannel发送Input给App-Android12
2、Activity的DecorView界面添加
关键流程
wm.addView(decor,...) -> WindowManagerGlobal.java#addView -> ViewRootImpl.java#setView -> Session extends IWindowSession.Stub#addToDisplayAsUser -> WindowManagerService.java#addWindow
,查看 Activity窗口的添加过程 了解,如下图查看,了解其中PhoneWindow.java
是Android-specific Window,实际ViewRootImpl
添加DecorView.java
到WMS
。
3、Activity和View处理InputEvent
3.1 InputEventReceiver接收InputEvent
添加窗口时,
ViewRootImpl.java
中InputChannel / WindowInputEventReceiver
建立联系;通过JNI调用,最终在WindowInputEventReceiver.java#onInputEvent
通过InputStage责任链
处理InputEvent
,可参照1中时序图流程查看代码。
InputStage责任链:
SyntheticInputStage - ViewPostImeInputStage - NativePostImeInputStage - EarlyPostImeInputStage - ImeInputStage - ViewPreImeInputStage - NativePreImeInputStage
,InputStage的各子类Input事件result = onProcess(q)处理,再判断是forward()向下传递,还是finish()结束。
- NativePreImeInputStage: 主要是为了将消息放到
NativeActivity
中去处理。- ViewPreImeInputStage: 最后会调用Acitivity的所有view的onkeyPreIme方法,这样就给View在输入法处理key事件之前先得到消息。
- ImeInputStage:
onProcess(q)
处理会调用InputMethodManager的dispatchInputEvent方法处理消息。- EarlyPostImeInputStage: 屏幕上有焦点的View会高亮显示,用来提示用户焦点所在。
- NativePostImeInputStage: 为了让IME处理完消息后能先于普通的Activity处理消息。
- ViewPostImeInputStage: Acitivity和view处理各种消息。
- SyntheticInputStage: 流水线的最后一级,经过层层过滤之后,到达这里的消息已经不多了,例如手机上的虚拟按键消息。
Activity和View的事件处理主要对应的InputStage是
ViewPostImeInputStage
。
onProcess(q)
中处理KeyEvent
:processKeyEvent(q)
onProcess(q)
中处理MotionEvent
:processPointerEvent(q)、processTrackballEvent(q)、processGenericMotionEvent(q)
@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);
}
}
}
3.2 处理KeyEvent
处理顺序如下:
- DecorView传递给
Activity#dispatchKeyEvent
处理mFocused
窗口的OnKeyListener
监听,在处理其onKeyDown()、onKeyLongPress()、onKeyUp()、onKeyMultiple()
方法dispatchUnhandledKeyEvent()
未拦截处理的KeyEvent发送给ViewGroup子View的OnUnhandledKeyEventListener
监听- 在处理Activity中的
onKeyDown()、onKeyLongPress()、onKeyUp()、onKeyMultiple()
方法- 未有任何窗口拦截处理,最后传递PhoneWindow的
onKeyDown()、onKeyUp()
frameworks/base/core/java/com/android/internal/policy/DecorView.java
public 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 key
if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
return true;
}
}
}
if (!mWindow.isDestroyed()) {
final Window.Callback cb = mWindow.getCallback();
final 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);
}
frameworks/base/core/java/android/view/KeyEvent.java
public final boolean dispatch(Callback receiver, DispatcherState state,
Object target) {
switch (mAction) {
case ACTION_DOWN: {
mFlags &= ~FLAG_START_TRACKING;
if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state
+ ": " + this);
boolean res = receiver.onKeyDown(mKeyCode, this);
if (state != null) {
if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {
if (DEBUG) Log.v(TAG, " Start tracking!");
state.startTracking(this, target);
} else if (isLongPress() && state.isTracking(this)) {
try {
if (receiver.onKeyLongPress(mKeyCode, this)) {
if (DEBUG) Log.v(TAG, " Clear from long press!");
state.performedLongPress(this);
res = true;
}
} catch (AbstractMethodError e) {
}
}
}
return res;
}
case ACTION_UP:
if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state
+ ": " + this);
if (state != null) {
state.handleUpEvent(this);
}
return receiver.onKeyUp(mKeyCode, this);
case ACTION_MULTIPLE:
final int count = mRepeatCount;
final int code = mKeyCode;
if (receiver.onKeyMultiple(code, count, this)) {
return true;
}
if (code != KeyEvent.KEYCODE_UNKNOWN) {
mAction = ACTION_DOWN;
mRepeatCount = 0;
boolean handled = receiver.onKeyDown(code, this);
if (handled) {
mAction = ACTION_UP;
receiver.onKeyUp(code, this);
}
mAction = ACTION_MULTIPLE;
mRepeatCount = count;
return handled;
}
return false;
}
return false;
}
3.3 处理MotionEvent
待续