Understanding of the Android event distribution process

Source: Internet
Author: User
Tags gety

I've seen a lot of people introduce the Android event dispatch process, but there have been a lot of errors in the recent use of those to write code. So review the entire process and simply describe the start of the touch screen from the hand to the event distributed in the View tree. Analyze the viewgroup.dispatchtouchevent from the source.

Events from touch to view overview

The Android event is generated from our touch screen, after the input subsystem, and finally to our application (or through Windowmanagerservice to reach the application).

and the input subsystem in the Java layer corresponds to Inputmanagerservice, which is mainly in the native layer, by Inputreader read Eventhub metadata, the data processed into InputEvent, Finally sent to Inputdispatcher, while Inputdispatcher is responsible for sending the time to the application, the input subsystem process can be found in this article Android framework--input subsystem.

The time flow for the application tier is mainly illustrated by the following flowchart:

The final step is what we often call the view event distribution process. In addition the above Decorview is passed two times, the first time is called Decorview dispatchtouchevent, its source code is:

    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        final Callback cb = getCallback();        return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)                : super.dispatchTouchEvent(ev);    }

Callback is the window.callback,activity implementation of this interface. In the activity's attach function, the setcallback of window is called and the activity is set to window. So the Getcallback returned here is the activity, which will eventually invoke the activity's dispatchtouchevent. Let's look at the dispatchtouchevent function of activity:

    public boolean dispatchTouchEvent(MotionEvent ev) {        if (ev.getAction() == MotionEvent.ACTION_DOWN) {            onUserInteraction();        }        if (getWindow().superDispatchTouchEvent(ev)) {            return true;        }        return onTouchEvent(ev);    }

The Onuserinteraction method is called when Action_down, and then the Superdispatchtouchevent of the window (which is actually Phonewindow) is called. If the superdispatchtouchevent of the window consumes the event, it is returned directly, and the activity's Ontouchevent method is not called.

@OverridepublicsuperDispatchTouchEventevent) {    return mDecor.superDispatchTouchEvent(event);}

And Decorview's superdispatchtouchevent is:

publicsuperDispatchTouchEventevent) {    return super.dispatchTouchEvent(event);}

Finally, the parent class of Dispatchtouchevent,decorview that calls Decorview's parent class is framelayout, which does not implement the method and eventually calls the Dispatchtouchevent method of ViewGroup. From here, we have entered the time distribution process of the view tree.

Event dispatch process for view tree

Here you analyze some of the features of the event distribution from the source. The event dispatch starts at the beginning of the ViewGroup dispatchtouchevent (Decorview parent Class), the following is an analysis of ViewGroup's dispatchtouchevent pseudo-code, annotated directly in the corresponding Code section:

"'
@Override
public boolean dispatchtouchevent (Motionevent ev) {
Start by doing some debugging validation, and if the object of the event is focused view, and the current view is a focused view,
It is possible that the view's child view will handle this event, so set the Targetaccessibilityfocus to False.
...

    Boolean handled = FALSE;        if (onfiltertoucheventforsecurity (EV)) {//Check if the event is a secure final int action = Ev.getaction ();        Final int actionmasked = action & motionevent.action_mask;        Handle an initial. if (actionmasked = = Motionevent.action_down) {//Throw away all previous state when starting a new touch Gestu            Re.  The framework may has dropped the up or cancel event for the previous gesture//due to an app switch, ANR,            or some other state change. Clears the previous event state. For example, when app switching, ANR or other state changes, the system framework will remove the up or cancel event.            The mfirsttouchtarget is emptied here, and mfirsttouchtarget is the view processing object that holds the event.            Cancelandcleartouchtargets (EV); Resettouchstate (); This will clear the Mgroupflags flag_disallow_intercept identity,//1.        Each time the event stream starts, the flag_disallow_intercept is cleared first, so the requestdisallowintercepttouchevent of the child view is only valid when the secondary event stream is active. }//Determine if it is necessary to intercept the event to determine if the event is a down event, or if there is a subclass that will handle the event (Mfirsttouchtarget is not null), and FLAG_DISALLOW_INTERCThe EPT is not set.        Final Boolean intercepted; if (actionmasked = = Motionevent.action_down | | Mfirsttouchtarget! = NULL) {Final Boolean Disall            Owintercept = (Mgroupflags & flag_disallow_intercept)! = 0;                if (!disallowintercept) {//2. Action_down is only called if there is touchtarget and is not disallow, or onintercepttouchevent.                intercepted = Onintercepttouchevent (EV); Ev.setaction (action);            Restore action in case it is changed} else {intercepted = false; }} else {///There is no touch targets and the This action is a initial down/so this VI            EW Group continues to intercept touches.        intercepted = true; }//If intercepted, start normal event dispatch.        Also If there is already//A view is handling the gesture and do normal event dispatch. if (intercepted | | Mfirsttouchtarget! = NULL) {EV.SETTARGETACCessibilityfocus (FALSE); }//Check for cancelation. To determine whether to cancel the final Boolean canceled = Resetcancelnextupflag (this) | |        actionmasked = = Motionevent.action_cancel;        Update list of touch targets for pointer down, if needed.        Final Boolean split = (Mgroupflags & flag_split_motion_events)! = 0;        Touchtarget newtouchtarget = null;        Boolean alreadydispatchedtonewtouchtarget = false;            if (!canceled &&!intercepted) {//If the event is a targeting accessiiblity focus we give it to the  View that have accessibility focus and if it does not handle it//We clear the flag and dispatch the            Event to all children as usual.             We is looking up the accessibility focused host to avoid keeping//state since these events is very rare. View Childwithaccessibilityfocus = Ev.istargetaccessibilityfocus ()? Findchildwithaccessibilityfocus (): null;            if (actionmasked = = Motionevent.action_down | | (split && actionmasked = = Motionevent.action_pointer_down) | | actionmasked = = motionevent.action_hover_move) {final int actionindex = Ev.getactionindex ();//Always 0 F or down final int idbitstoassign = split?                1 << Ev.getpointerid (actionindex): Touchtarget.all_pointer_ids;                Clean up earlier touch targets for the pointer ID in case they//has become out of sync.                Removepointersfromtouchtargets (idbitstoassign);                Final int childrencount = Mchildrencount;                    if (Newtouchtarget = = NULL && Childrencount! = 0) {final float x = Ev.getx (Actionindex);                    Final float y = ev.gety (Actionindex);                    Find a child that can receive the event.       Scan children from front to back.             Final arraylist<view> preorderedlist = Buildorderedchildlist ();                    Final Boolean customorder = Preorderedlist = = null && ischildrendrawingorderenabled ();                    Final view[] children = Mchildren;                                for (int i = childrenCount-1; I >= 0; i--) {final int childindex = Customorder ?                        Getchilddrawingorder (Childrencount, i): i; Final View child = (Preorderedlist = = null)?                        Children[childindex]: Preorderedlist.get (Childindex); If there is a view that have accessibility focus we want it/to get the event first and if not Handled we'll perform a//normal dispatch.                        We may does a double iteration but this is//safer given the timeframe. if (childwithaccessibilityfocus! = null) {if (Childwithaccessibilityfocus! = child) {continue;                            } childwithaccessibilityfocus = null;                        i = childrenCount-1; }//Determines whether the current child can receive an event, and whether the event is in the current view range if (!canviewreceivepointerevents (CHI LD) | |                            !istransformedtouchpointinview (x, y, child, null)) {Ev.settargetaccessibilityfocus (false);                        Continue                        } Newtouchtarget = Gettouchtarget (child);                            if (newtouchtarget! = null) {//already receiving touch within its bounds.                            Give it the new pointer in addition to the ones it is handling.     Newtouchtarget.pointeridbits |= idbitstoassign;                       Break                                    } resetcancelnextupflag (child); 3.dispatchTransformedTouchEvent converts the event into a child coordinate space (a change of getx), then removes the unrelated points ID and, if necessary, changes the event, Finally call Child.dispatchtouchevent. Dispatchtransformedtouchevent sends the event to child, and if child successfully processes it, the child is added to the mfirsttouchtarget if (Dispatchtran Sformedtouchevent (EV, FALSE, Child, Idbitstoassign)) {//child wants to receive touch within I                            TS bounds.                            Mlasttouchdowntime = Ev.getdowntime ();                                 if (preorderedlist! = null) {//Childindex points into presorted list, find original index for (int j = 0; J < Childrencount; J + +) {if (Childr                                        En[childindex] = = Mchildren[j]) {mlasttouchdownindex = J;   Break                                 }}} or else {                            Mlasttouchdownindex = Childindex;                            } Mlasttouchdownx = Ev.getx ();                            Mlasttouchdowny = Ev.gety ();  Addtouchtarget will create a new touchtarget and add it to mfirsttouchtarget Newtouchtarget = Addtouchtarget (Child,                            Idbitstoassign);                            Alreadydispatchedtonewtouchtarget = true;                        Break }//The accessibility focus didn ' t handle the event, so clear//the flag a                        nd do a normal dispatch to all children.                    Ev.settargetaccessibilityfocus (FALSE);                } if (preorderedlist! = null) preorderedlist.clear (); } if (Newtouchtarget = = null && MfirsttoucHtarget! = null) {//did not ' find a ' 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; }}}//4.        Dispatch events//Dispatch to touch targets.            if (Mfirsttouchtarget = = null) {//mfirsttouchtarget is null to indicate that no child view will handle this event, it is handed to the current viewgroup 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) {//iterates through the Mfirsttouchtarget list, processing the Touchtarget one by one.                Final Touchtarget next = Target.next;                    if (alreadydispatchedtonewtouchtarget && target = = Newtouchtarget) {//new additions will not be processed immediately, Action_down has been dispatched in front                handled = TRUE;  } else {Final Boolean cancelchild = Resetcancelnextupflag (target.child) | |                    intercepted; Dispatchtransformedtouchevent will convert the event into the child coordinate space (getx change) and then remove the unrelated points ID, if necessary to change the event,                            Finally call Child.dispatchtouchevent if (dispatchtransformedtouchevent (EV, Cancelchild,                    Target.child, target.pointeridbits)) {handled = true; } if (Cancelchild) {if (predecessor = = null) {Mfirs                        Ttouchtarget = 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.getacti            Onindex ();            final int idbitstoremove = 1 << ev.getpointerid (actionindex);        Removepointersfromtouchtargets (Idbitstoremove); }} ...//code for testing return handled;} The ' event dispatch process is mainly focused on the invocation of these methods:-dispatchtouchevent This is the event that dispatches each view when the first method is called, if it is Viewgroup,dispatchtoucHevent will first call Onintercepttouchevent if the event should be intercepted, and if not intercepted, the child view will first determine if there is a processing of the event (in Action_ The Mfirsttouchtarget link in Down is used to save the touchtarget that handles the event, and if not, the ontouchevent of the current view is called. -Onintercepttouchevent to determine if an event should be intercepted, viewgroup default implementation is to return false, child view can call Getparent.requestdisallowintercepttouchevent () To block the parent view interception. Otherwise, the method will be called whenever a child view may consume events. -Ontouchevent This method is called when no child view will consume events, Onclicklistener,ontouchlistener,onlongclicklistener is handled in this method. If true indicates consumption of this event. For the event dispatch process, I think there are several places to be aware of: 1. The ViewGroup and the child view that it contains should be considered as part of this viewgroup, and whether or not a viewgroup will handle an event should be the child view that contains it. 2. If the entire viewgroup and its subclasses do not have a view handling Action_down event, the ViewGroup dispatchtouchevent will not be called the next time. However, if Action_down returns True, the next event will continue to be distributed to viewgroup even if a action_move in the middle returns false***. 3. Onintercepttouchevent is called when the child view may handle the event and is not set flag_disallow_intercept. Normally, it must be called when Action_down, because Resettouchstate () is called first when Action_down.

This is a little bit changed a long time ago wrote a imitation QQ mailbox sliding exit Write a thing: Https://github.com/xxxzhi/SlideListener, change the process found before the things to understand not good enough ...

Source code is the most fundamental reason to explain many phenomena, reading source can better understand the event distribution process, more profound understanding.

Understanding of the Android event distribution process

Related Article

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.