Overall process of event distribution
ViewRootImpl is followed by the timing of the client. This paper mainly focuses on the process of event distribution in the client
ViewRootImpl distribution event
dispatchInputEvent(InputEvent event) is the starting point of ViewRootImpl distribution event, and event is the input event
- After receiving the input event, ViewRootImpl will add it to the message queue using enqueueInputEvent(), and obtainQueuedInputEvent() will convert the input event into the QueuedInputEvent data structure required by the queue
- All messages from the message queue are handled by the deliverInputEvent method in doProcessInputEvents()
- The deliverInputEvent() method uses the InputStage linked list structure and the responsibility chain mode to traverse and transfer events
On Android view. In viewrootimpl#deliverinputevent, the relevant code is implemented as follows
InputStage stage; if (q.shouldSendToSynthesizer()) { stage = mSyntheticInputStage; } else { stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; } if (q.mEvent instanceof KeyEvent) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "preDispatchToUnhandledKeyManager"); try { mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } if (stage != null) { handleWindowFocusChanged(); stage.deliver(q); } else { finishInputEvent(q); }
InputStage is in viewrootimpl Java #setview (view, WindowManager. Layoutparams, view, int) is initialized
// 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;
SyntheticInputStage and ViewPostImeInputStage inherit from InputStage, and rewrite the following construction method
public InputStage(InputStage next) { mNext = next; }
It can be seen that different inputstages build a linked list structure, and its head node is referenced by mFirstInputStage. Each node contains a next pointer to the next InputStage
Here is a brief description of each InputStage:
InputStage | explain |
---|---|
NativePreImeInputStage | Distribute inputevents earlier than IME to NativeActivity for processing. NativeActivity has the same functions as ordinary acitivity, but it is implemented in the native layer, so the execution efficiency will be higher. At the same time, NativeActivity is very practical in game development (does not support touch events). |
ViewPreIMEInputStage | When inputevents earlier than IME are distributed to the view framework for processing, the onkeypreime method of view (input focus) will be called. At the same time, the view will get messages before the input method processes key events and give priority to processing. View series controls can directly copy onkeypreime (touch events are not supported). |
ImeInputStage | Distribute InputEvent to IME for processing, call onProcess of ImeInputStage, and the dispatchInputEvent method of InputMethodManager for processing messages (touch events are not supported). |
EarlyPostImeInputStage | It is related to touchmode. For example, your mobile phone has a direction key. Pressing the direction key will exit touchmode. This event is consumed. There may be a change in the background of the view, but it is uncertain (touch events are supported). |
NativePostImeInputStage | Distribute the InputEvent event to the nativeactivity. After the IME processes the message, it can process the message before the normal Activity (touch event is supported at this time). |
ViewPostImeInputStage | Distribute the InputEvent event to the view framework, and the event distribution of view (supports touch events). Finally, three methods will be called to the input focus: onKey of the listener registered with setKeyListener, followed by onKeyDown and onKeyUp, or onKeyDown and onKeyUp methods of activity, that is, handle the unattended key events. |
SyntheticInputStage | InputEvent was not processed. |
On Android view. In viewrootimpl#deliverinputevent, if the stage is not null, Android. Net will be executed view. ViewRootImpl. Inputstage#deliver method
if (stage != null) { handleWindowFocusChanged(); stage.deliver(q); } else { finishInputEvent(q); }
android. view. ViewRootImpl. The inputstage#deliver method is implemented as follows
public final void deliver(QueuedInputEvent q) { if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) { forward(q); } else if (shouldDropInputEvent(q)) { finish(q, false); } else { traceEvent(q, Trace.TRACE_TAG_VIEW); final int result; try { result = onProcess(q); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } apply(q, result); } }
Events pass through each Stage in turn. If the event is not marked as "Finished", the Stage will process it, and then return the processing result forward or Finish. Forward runs the next Stage to continue processing, and Finished will set the "Finished" mark on the event and advance to the next Stage until the last level of SyntheticInputStage.
Stage calls onProcess to process its events. Template mode is adopted here. onProcess is a hook method. Different subclasses have different implementations. For touch screen events and key press events, ViewPostImeInputStage is mainly used to process them.
ViewPostImeInputStage
android.view.ViewRootImpl.ViewPostImeInputStage#onProcess
@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); } } }
The onProcess method is divided according to different event types. The touch-screen event goes through processPointerEvent(), and the key event goes through processKeyEvent()
Take a touch screen event as an example,
android.view.ViewRootImpl.ViewPostImeInputStage#processPointerEvent
private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; mAttachInfo.mUnbufferedDispatchRequested = false; mAttachInfo.mHandlingPointerEvent = true; boolean handled = mView.dispatchPointerEvent(event); maybeUpdatePointerIcon(event); maybeUpdateTooltip(event); mAttachInfo.mHandlingPointerEvent = false; if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) { mUnbufferedInputDispatch = true; if (mConsumeBatchedInputScheduled) { scheduleConsumeBatchedInputImmediately(); } } return handled ? FINISH_HANDLED : FORWARD; }
Where Boolean handled = Mview dispatchPointerEvent(event); Transfer the event to the View under ViewRootImpl for processing
android.view.View#dispatchPointerEvent
@UnsupportedAppUsage public final boolean dispatchPointerEvent(MotionEvent event) { if (event.isTouchEvent()) { return dispatchTouchEvent(event); } else { return dispatchGenericMotionEvent(event); } }
Judge in dispatchPointerEvent(). If it is a touch event, go to dispatchTouchEvent(), otherwise execute dispatchGenericMotionEvent()
The sequence diagram of the above process is
Reference blog
https://www.cnblogs.com/huan89/p/14286463.html
http://gityuan.com/2016/12/31/input-ipc/
https://blog.csdn.net/vviccc/article/details/93377708