Android event distribution mechanism from the source code

Source: Internet
Author: User

Android event distribution mechanism from the source code

In the setView method of ViewRootImpl, the user's touch button message is reflected in the form, while windowManagerService is to manage these windows. Once it receives the user's touch button message for the form, the corresponding action must be reflected in the specific view. In Android, a specific interface is presented by an Activity, and the Activity contains a window, this window contains a phoneWindow, which is a real window. It encapsulates a framework layout and provides specific window operation interfaces, phoneWindow contains a DecorView, which is the ui containing the entire Activity and will be attach to the main Activity window. Therefore, the messages that the user uses to touch the buttons are captured by windowManagerService and sent to the DecorView in phoneWindow for corresponding processing. The bridge connecting the two is a ViewRoot class, And the ViewRoot class is created by windowManagerService, there is a W class in it. This W class is a binder responsible for calling the ipc of WindowManagerService. After W receives the message sent by windowManagerService, it passes the message to ViewRoot, then it is passed to ActivityThread for parsing and processing,

1. In the setView method of ViewRootImpl. java class:

 

res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,                            getHostVisibility(), mDisplay.getDisplayId(),mAttachInfo.mContentInsets, mInputChannel);
The mWindow is passed to the window, which is an example of the W class. 2. When a specific touch button message occurs, it will be received by the onInputEvent method of the WindowInputEventReceiver class in the ViewRootImpl class, which is actually a callback.
public void onInputEvent(InputEvent event) {        enqueueInputEvent(event, this, 0, true); }
Void enqueueInputEvent (InputEvent event, InputEventReceiver identifier er, int flags, boolean processImmediately) {paiq = events (event, aggreger, flags); // 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 encoded Ed and we cannot trust that // the time stamp of injected events are monotonic. queuedInputEvent last = mFirstPendingInputEvent; if (last = null) {mFirstPendingInputEvent = q;} else {while (last. mNext! = Null) {last = last. mNext;} last. mNext = q ;}if (processImmediately) {// immediately process the event doProcessInputEvents () ;}else {// put the event to the last scheduleProcessInputEvents ();}}

In the enqueueInputEvent method, add the event to the end of the queue. If processImmediately is true, the doProcessInputEvents method is called directly. Otherwise, the deliverInputEvent method is called, distribute input events. In this method, if it is a key event, such as back and home, it will call the deliverKeyEvent distribution event. Some general mobile events call the deliverGenericMotionEvent method.
Private void deliverInputEvent (QueuedInputEvent q) {Trace. traceBegin (Trace. TRACE_TAG_VIEW, deliverInputEvent); try {if (q. mEvent instanceof KeyEvent) {// if it is a key event, that is, the back, home, and other buttons, The deliverKeyEvent (q);} else {// touch event final int source = q. mEvent. getSource (); if (source & InputDevice. SOURCE_CLASS_POINTER )! = 0) {deliverPointerEvent (q);} else if (source & InputDevice. SOURCE_CLASS_TRACKBALL )! = 0) {deliverTrackballEvent (q);} else {deliverGenericMotionEvent (q) ;}} finally {Trace. traceEnd (Trace. TRACE_TAG_VIEW );}}
In the deliverPointerEvent method, if the view object does not exist or is not added, this event will not be processed and will be directly finishInputEvent. Then judge whether the action is MotionEvent. ACTION_DOWN. If yes, it indicates that the touch mode has changed and windowManager needs to be told to process the event locally, because each click operation represents the arrival of a new event. Record the touch position, which indicates which view should be used to receive the event, and then directly call mView. dispatchPointerEvent (event) to distribute the event. If the event is distributed, it ends.
Private void deliverPointerEvent (QueuedInputEvent q) {final MotionEvent event = (MotionEvent) q. mEvent; final boolean isTouchEvent = event. isTouchEvent (); if! = Null) {if (isTouchEvent) {mInputEventConsistencyVerifier. onTouchEvent (event, 0);} else {mInputEventConsistencyVerifier. onGenericMotionEvent (event, 0) ;}// If there is no view, then the event will not be handled. if (mView = null |! MAdded) {// the view object is empty or not added. This event will not be processed by finishInputEvent (q, false); return ;}// Translate the pointer event for compatibility, if needed. if (mTranslator! = Null) {mTranslator. translateEventInScreenToAppWindow (event);} // Enter touch mode on down or scroll. final int action = event. getAction (); if (action = MotionEvent. ACTION_DOWN | action = MotionEvent. ACTION_SCROLL) {// if it is a MotionEvent. ACTION_DOWN // If the touch mode is changed, tell wm to process it locally. // each click operation is a change of the touch event ensureTouchMode (true );} // Offset the scroll position. if (mCurScrollY! = 0) {event. offsetLocation (0, mCurScrollY);} if (MEASURE_LATENCY) {lt. sample (A Dispatching PointerEvents, System. nanoTime ()-event. getEventTimeNano ();} // Remember the touch position for possible drag-initiation. // It is possible to drag and drop to start and record the touch position if (isTouchEvent) {mLastTouchPoint. x = event. getRawX (); mLastTouchPoint. y = event. getRawY ();} // Dispatch touch to view hierarchy. // distribute events to views at the view level // DecorView inherits FrameLayout, which indirectly inherits ViewGroup, view // DoverView ----> Activity --> PhoneWindow ---> DocerView ----> ViewGroup boolean handled = mView. dispatchPointerEvent (event); if (MEASURE_LATENCY) {lt. sample (B Dispatched PointerEvents, System. nanoTime ()-event. getEventTimeNano ();} if (handled) {// finishInputEvent (q, true); return;} // Pointer event was unhandled. // indicates that the event has been handled by finishInputEvent (q, false );
There is no dispatchPointerEvent method in DecorView. Therefore, View. java's dispatchPointerEvent method is called to determine the specific type of event, and then its own dispatchTouchEvent is called.
Public final boolean dispatchPointerEvent (MotionEvent event) {if (event. isTouchEvent () {// if it is a touch event return dispatchTouchEvent (event);} else {// if it is a general mobile event return dispatchGenericMotionEvent (event );}}

In the dispatchTouchEvent method of DecorView. java, if the Activity is not empty and is not destroyed, the dispatchTouchEvent method of the Activity is called. Otherwise, the dispatchTouchEvent method of the parent class View is called.
@ Override public boolean dispatchTouchEvent (MotionEvent ev) {// callback is the final Callback cb = getCallback (); // if the Activity is not empty and is not destroyed, call the dispatchTouchEvent of the Activity // otherwise, call the dispatchTouchEvent return cb of the parent class! = Null &&! IsDestroyed () & mFeatureId <0? Cb. dispatchTouchEvent (ev): super. dispatchTouchEvent (ev );}
In the dispatchTouchEvent method of the Activity, the superDispatchTouchEvent method of PhoneWindow is called to distribute the event. If all views in the Activity do not process the event, the OnTouchEvent of the Activity is processed.
Public boolean dispatchTouchEvent (MotionEvent ev) {if (ev. getAction () = MotionEvent. ACTION_DOWN) {// do some operations before dispatch. In fact, there is no onUserInteraction ();} // call superDispatchTouchEvent in PhoneWindow // superDispatchTouchEvent in PhoneWindow directly calls super in superDispatchTouchEvent method of mDecor. dispatchOnTouchEvent // that is, it begins to enter the dispatchOnTouchEvent method if (getWindow () in the viewGroup (). superDispatchTouchEvent (ev) {return true;} // when all views in the Activity do not process the Event, the onTouchEvent () of the Activity is used () to process // notify window to close this touch event return onTouchEvent (ev );}

 public boolean onTouchEvent(MotionEvent event) {        if (mWindow.shouldCloseOnTouch(this, event)) {            finish();            return true;        }                return false;    }
In the superDispatchTouchEvent of PhoneWindow, The superDispatchTouchEvent method of DecorView is called, and the super. dispatchTouchEvent (event) method is called, that is, the event distribution starts in viewGroup.
 @Override    public boolean superDispatchTouchEvent(MotionEvent event) {        return mDecor.superDispatchTouchEvent(event);    }

 public boolean superDispatchTouchEvent(MotionEvent event) {            return super.dispatchTouchEvent(event);        }
In the dispatchTouchEvent of ViewGroup, the processing is as follows. If the action is MotionEvent. ACTION_DOWN, You need to reset the touch status. Then determine whether to intercept the event, and traverse all the children to find a child who can receive the event. If the child does not exist in TouchTarget, the event is distributed to the child view, this search is based on the view area. If childView does not consume this event, the event that you are handling will be traced back to the parent view if it is not processed by yourself. Otherwise, viewGroup will pass the first-level recursion of the event, if child is a viewGroup, repeat the preceding steps. If child is a view, call the dispatchTouchEvent method directly.
Public boolean dispatchTouchEvent (MotionEvent ev) {if (mInputEventConsistencyVerifier! = Null) {mInputEventConsistencyVerifier. onTouchEvent (ev, 1);} boolean handled = false; if (onFilterTouchEventForSecurity (ev) {final int action = ev. getAction (); final int actionMasked = action & MotionEvent. ACTION_MASK; // Handle an initial down. if (actionMasked = MotionEvent. ACTION_DOWN) {// Throw away all previous state when starting a new touch gesture. // The framework may have dropped t He up or cancel event for the previous gesture // due to an app switch, ANR, or some other state change. // ACTION_DOWN means that the touch event changes, so you need to clear the TouchTargets and TouchState, mFirstTouchTarget = null cancelAndClearTouchTargets (ev); // reset the touch status resetTouchState ();} // Check for interception. final boolean intercepted; if (actionMasked = MotionEvent. ACTION_DOWN | mFirstTouchTarget! = Null) {final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT )! = 0; if (! DisallowIntercept) {// interception event. The default value false indicates that no interception is performed. If yes, intercepted = onInterceptTouchEvent (ev); ev is executed below. setAction (action); // restore action in case it was changed} else {intercepted = false ;}} else {// There are no touch targets and this action is not an initial down // so this view group continues to intercept touches. intercepted = true;} // Check for cancelation. final boolean canceled = ResetCancelNextUpFlag (this) | actionMasked = MotionEvent. ACTION_CANCEL; // Update list of touch targets for pointer down, if needed. final boolean split = (mGroupFlags & tags )! = 0; TouchTarget newTouchTarget = null; boolean alreadyDispatchedToNewTouchTarget = false; if (! Canceled &&! Intercepted) {if (actionMasked = MotionEvent. ACTION_DOWN | (split & actionMasked = MotionEvent. ACTION_POINTER_DOWN) | actionMasked = MotionEvent. ACTION_HOVER_MOVE) {final int actionIndex = ev. getActionIndex (); // always 0 for down final int idBitsToAssign = split? 1 <ev. getPointerId (actionIndex): TouchTarget. ALL_POINTER_IDS; // Clean up earlier touch targets for this pointer id in case they // have become out of sync. removePointersFromTouchTargets (idBitsToAssign); final int childrenCount = mChildrenCount; if (childrenCount! = 0) {// Find a child that can receive the event. // Scan children from front to back. // traverse all the children to find a child who can receive the event // The Child final View in a region [] children = mChildren; final float x = ev. getX (actionIndex); final float y = ev. getY (actionIndex); final boolean customOrder = isChildrenDrawingOrderEnabled (); for (int I = childrenCount-1; I> = 0; I --) {final int childIndex = customOrder? GetChildDrawingOrder (childrenCount, I): I; final View child = children [childIndex]; if (! CanViewReceivePointerEvents (child) |! IsTransformedTouchPointInView (x, y, child, null) {continue;} // determines whether the child is in TouchTarget newTouchTarget = getTouchTarget (child); if (newTouchTarget! = Null) {// exist // Child is already refreshing ing touch within its bounds. // Give it the new pointer in addition to the ones it is handling. newTouchTarget. pointerIdBits | = idBitsToAssign; break;} resetCancelNextUpFlag (child); // if the child does not have TouchTarget, call dispatchTransformedTouchEvent // distribute the event to the subview, you should find the touch region if (dispatchTransformedTouchEvent (ev, false, child, idBitsToAssign) {// Child Wants to receive touch within its bounds. mLastTouchDownTime = ev. getDownTime (); mLastTouchDownIndex = childIndex; mLastTouchDownX = ev. getX (); mLastTouchDownY = ev. getY (); newTouchTarget = addTouchTarget (child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true; break ;}} if (newTouchTarget = null & mFirstTouchTarget! = Null) {// Did not find a child to receive the event. // Assign the pointer to the least recently added target. newTouchTarget = mFirstTouchTarget; while (newTouchTarget. next! = Null) {newTouchTarget = newTouchTarget. next;} newTouchTarget. pointerIdBits | = idBitsToAssign ;}}// Dispatch to touch targets. if (mFirstTouchTarget = null) {// mFirstTouchTarget is empty, indicating that childview did not consume this event, then it handles this event by itself. // if viewGroup does not process it by itself, then it goes back to the parent view for processing // No touch targets so treat this as an ordinary view. handled = dispatchTransformedTouchEvent (ev, canceled, null, TouchTarget. ALL_POINTER_IDS );} Else {// Dispatch to touch targets, excluding the new touch target if we already // dispatched to it. cancel touch targets if necessary. touchTarget predecessor = null; TouchTarget target = mFirstTouchTarget; while (target! = Null) {final TouchTarget next = target. next; if (alreadyDispatchedToNewTouchTarget & target = newTouchTarget) {handled = true;} else {final boolean cancelChild = resetCancelNextUpFlag (target. child) | intercepted; // viewGroup recursively transmits events. If child is a gourp, repeat the preceding steps. // if child is a gourp, directly call the dispatchTouchEvent method if (dispatchTransformedTouchEvent (ev, cancelChild, target. child, target. pointerIdBits) {handled = True;} if (cancelChild) {if (predecessor = null) {mFirstTouchTarget = next;} else {predecessor. next = next;} target. recycle (); target = next; continue ;}} predecessor = target; target = next ;}// Update list of touch targets for pointer up or cancel, if needed. if (canceled | actionMasked = MotionEvent. ACTION_UP | actionMasked = MotionEvent. ACTION_HOVER_MOVE) {resetTouchState ();} Else if (split & actionMasked = MotionEvent. ACTION_POINTER_UP) {final int actionIndex = ev. getActionIndex (); final int idBitsToRemove = 1 <ev. getPointerId (actionIndex); removePointersFromTouchTargets (idBitsToRemove) ;}} if (! Handled & mInputEventConsistencyVerifier! = Null) {mInputEventConsistencyVerifier. onUnhandledEvent (ev, 1);} return handled ;}
In View. java's dispatchTouchEvent method, if the listener has been registered and is enable, And the onTouch of the listener returns true, onTouchEvent will not be called.
Public boolean dispatchTouchEvent (MotionEvent event) {if (mInputEventConsistencyVerifier! = Null) {mInputEventConsistencyVerifier. onTouchEvent (event, 0);} if (onFilterTouchEventForSecurity (event) {// noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li! = Null & li. mOnTouchListener! = Null & (mViewFlags & ENABLED_MASK) = ENABLED & li. mOnTouchListener. onTouch (this, event) {// to execute the onTouchEvent method, if one of the preceding three conditions is not met, you can return true;} if (onTouchEvent (event )) {return true;} // if all views do not process TouchEvent, the Activity will process it.} if (mInputEventConsistencyVerifier! = Null) {mInputEventConsistencyVerifier. onUnhandledEvent (event, 0);} return false ;}

If all views in the Activity do not process this event, the Activity will handle it by itself.
public boolean onTouchEvent(MotionEvent event) {        if (mWindow.shouldCloseOnTouch(this, event)) {            finish();            return true;        }                return false;    }

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.