Android event distribution Abstraction — the hook model





If you are not sure about the above question, this article will tell you the answer.

From the perspective of “problem driven understanding”, this paper attempts to sum up the intricate event distribution mechanism in a word — the hook model, which is like a fish hook to meet the edge and solve various problems of event distribution mechanism.

It contains lots of deep and interesting pictures to sweeten the boring principle analysis.

User experience Little sister cleverly uses the limited screen space of the phone, perfectly designed simple and practical interaction functions, if you ask “how”? The answer must begin with the masterful use of event distribution mechanisms. In my Android application business development career, I have been exposed to the use of event distribution mechanisms and custom controls to build interactive and elegant pictures of business features. The picture below is my representative works of “list drag Sorting” and “card drawer effect” developed on mobile Baidu App and Meituan App respectively.

In 2018, at the peak of my coding skills and tactics, I creatively applied the MECE (Mad Exclusive, Collectively Exhaustive) analysis method to professional technical principle analysis. The “positive hard hard” event distribution mechanism writes down the Android event distribution – the ins and outs, and then once self inflated event distribution “dare not say master” (the joy of the app monkey is so simple).

You must know that one mountain is higher than the other. This year, I practiced the basic skills through “book to school” and studied The Art Exploration of Android Development by Yu Gangge. Several questions in the book “side-roundabout” severely hit the event distribution mechanism I had mastered “Not too good”.

It suddenly made me realize that “question-driven understanding” is a simple and effective way to learn, so I tried it.

Test your

Question, who wouldn’t? You can open your mouth, but we need someone who can test your level. This reminds me of an unforgettable thing in my work — writing online fault CaseStudy. I believe that those who have experienced it will never forget the “5 Whys” that hit the heart of the soul (asking five Whys one by one for the reasons of the problem, almost from the appearance of things to the essence).

Of course, we don’t have to be so hard on ourselves. I think we need to ask about three. Questions, also can not be too wordy, the avenue to simple, it is best to approach the essence of things from the most “simple” questions. I try to construct a simple scenario that deduces three big questions and a few small ones to help me understand the mechanics of progressive event distribution. The page has a 300*300 blue background FrameLayout and a 100*100 red background TextView in the center. As shown below: ▼

The next problem is simply to say the event distribution related method calls around the order of the FrameLayout and TextView controls. Because the scenario is fixed, there is no if, i.e. the answer corresponds to a unique path, there is no if… Just… For the sake of understanding, before answering the above questions, LET me introduce the core methods and corresponding functions of the event distribution mechanism:

DispatchTouchEvent: control event distribution body logic. This method in View is used to call onTouchListener. onTouch and onTouchEvent. This method in ViewGroup is used to determine whether to intercept or not. {:.warning}

OnInterceptTouchEvent: Whether to intercept events. If an event is intercepted, it is not distributed to child controls but consumed directly by itself. {:.warning}

OnTouchEvent: consumer event subject logic for handling keys state, an OnClickListener. OnClick and OnLongClickListener onLongClick. {:.warning}

A few lines of pseudo-code of Jade gang elder brother have been integrated into the above three core methods. To help you revisit the classic. With a little thought of his own, see lines 12 and 13.

Based on the pseudocode embedded above, I draw a flow chart, as shown below: ▼

Chapter 3, Page 142, explores the Art of Android Development with an easy-to-understand example that illustrates the mechanics of event distribution.

If the click event is a problem and the problem is eventually assigned to a programmer (this is the event distribution process),


As a result, the programmer can’t figure it out (onTouchEvent returns false). Now what?


The problem has to be solved, and that can only be solved by a higher level supervisor (whose onTouchEvent is called),


If the superior can not do it again, it can only be given to the superior superior to solve, so the problem layer upon layer to throw up.


This is a very common problem solving process within the company.

No buttons to listen for click distribution

1. Do not set the key listening, click in the red area, say which control is called which event distribution related methods? {:.info}

This is a simple question that doesn’t need to be repeated, and the answer is as follows (the first line indentation indicates that the current method is called from within the previous method) :

(1) Call FrameLayout dispatchTouchEvent (dispatchTouchEvent) in ViewGroup.

② Call the onInterceptTouchEvent from FrameLayout. Because there is no override event interception, default false is returned.

(3) Call TextView dispatchTouchEvent, which corresponds to the View dispatchTouchEvent method.

④ Call onTouchEvent of TextView. Because onInterceptTouchEvent is only available in ViewGroup, TextView is not ViewGroup, and there is no event interceptor. The default false is returned because the associated keystroke listening for consumption events is not set.

⑤ Call FrameLayout’s super.dispatchTouchEvent, which corresponds to the dispatchTouchEvent method in the View. Because the child control TextView does not consume events, FrameLayout attempts to consume events.

⑥ Call FrameLayout onTouchEvent. The default false is returned because the associated keystroke listening for consumption events is not set.

I believe this problem is not difficult for most students. But is the problem over? As we all know, normal click events include DOWN events and UP events. The above is only about DOWN events. What about UP events?

1.1 Since no one consumes DOWN events, can UP events still be distributed to FrameLayout? If not, where did the UP event go? {:.info}

In fact, when I first asked myself this question, I did not answer it.

Before answering that question, it’s worth reminding the Android developer documentation about Consistency Guarantees:

Press start, which may be accompanied by movement, until release or cancel ends. DOWN → MOVE(*) → UP/CANCEL. {:.warning}

To put it simply, a stream of events is like a train. The front and the back are required, and the middle cars are optional, if any. A DOWN event corresponds to a locomotive, an UP or CANCEL corresponds to the tail of a train, and a MOVE event corresponds to a railroad car. The familiar onClick keystroke listener is a complete stream of events that collectively determine whether or not a response is triggered. The event flow train model is shown below: ▼

If neither the control nor its descendants consume DOWN events, the control does not receive subsequent event streams. The metaphor in Android Development Art Quest is very vivid: your boss gives you something to do, and if you fail, there is no chance, because you only get one chance.

By this logic, the DOWN event does not consume, so it should not receive the UP event. If so, then the question is, where did the UP event go? After all, no control consumes an UP event.

Intuitively, it might be consumed for the Activity, so by customizing the Activity’s dispatchTouchEvent and onTouchEvent, FrameLayout dispatchTouchEvent, onInterceptTouchEvent and onTouchEvent, FrameLayout dispatchTouchEvent and onTouchEvent, Add the log and click.

The answer is clear: The UP event will continue to call the Activity’s dispatchTouchEvent and onTouchEvent, but not FrameLayout and TextView.

For those of you who have read the source code, there is no event distribution logic for an Activity. Instead, you end up calling event distribution in a DecorView, which inherits from a ViewGroup. The event distribution body logic is still done by the ViewGroup and View. The key response call is shown as follows: ▼

Therefore, there is a high probability that the event will be consumed by the DecorView. If you continue to guess, it’s a little inefficient. The most direct and effective way is to Debug the source code.

Set compileSdkVersion and targetSdkVersion to the same version as the Android emulator in build.gradle and download the source code during Debug.

Next, it’s just a matter of time.

By the way, never break in a ViewGroup or View. Doing so can easily break your heart… All controls inherit views (including viewGroups), and your Activity setContentView is not the entire View tree. Status bars, navigation bars, etc., are part of the content of the page, and the system does this for you. The page layout is as follows: ▼

Scientific operation is first through the log to find out the situation, find the rule, and then control the situation, targeted, through the custom control rewrite related methods, in the custom control break point, broken single point follow-up, accurate view of logic.

Details suggest readers to practice again, I directly said the result:

(1) DOWN events: TextView and FrameLayout do not consume DOWN events and are passed back up to the DecorView to call the DecorView’s onTouchEvent. But the DecorView also does not consume, and continues to pass to the Activity, calling the Activity’s onTouchEvent, and the Activity returns false. In short, DOWN events are continuously called to the DecorView and Activity and are never consumed. ② UP event: The Activity’s dispatchTouchEvent is called first and then the DecorView’s dispatchTouchEvent is called. Since mFirstTouchTarget is null, onInterceptTouchEvent is not called, but the intercepted status bit is set to true. The execution logic is shown in the dispatchTouchEvent source code fragment in the ViewGroup below, lines 4 and 16. Then call the DecorView’s onTouchEvent. Obviously, the DecorView does not consume either, and passes it to the Activity. Call the Activity’s onTouchEvent. In short, the UP event is also not consumed, and only the DecorView and Activity’s event-dispatch methods are called, and no other controls receive the event-dispatch calls. {:.success}

The dispatchTouchEvent logic source code fragment in ViewGroup is shown as follows: ▼

This question seems to be simple, but the person who can answer it is the real master.

Draw a sequence diagram to summarize: ▼

But some of you might ask, well, it doesn’t really matter if you don’t have keystroke listening, and most people don’t care about that. Let’s do another one.

Set key monitor & intercept click distribution

  1. FrameLayout and TextView are both set to monitor buttons, requiring clicking anywhere in the red and blue areas. Only FrameLayout buttons monitor responses.

{:.info}

That’s easy. I’ll do it!

Overwriting FrameLayout’s onInterceptTouchEvent method returns true. {:.success}

There’s nothing wrong with the answer. The onInterceptTouchEvent may be called from both the DOWN event and the UP event. The answer to the onInterceptTouchEvent does not distinguish between DOWN and UP, but simply returns true. Does the DOWN event have to return true? Can I return false? What about the UP event? Do I need to return true?

2.1 Can ONLY Intercept DOWN Events? Does the UP event need to return true after intercepting the DWON event? {:.info}

Click OnClickListener to listen to OnClickListener

OnClick is invoked by the onTouchEvent of the UP event, but only if PFLAG_PRESSED presses the status bit, which is done in the DOWN event. This explains the continuity of the flow of events. What about the MOVE event? This is problem number three. I’m going to press no. {:.warning}

Based on the above theory, the DOWN event must be intercepted. Return false; return true; return false;

Common sense dictates that a call to a pure function that returns a Boolean is redundant if it returns true and false. OnInterceptTouchEvent is basically a pure function of this kind. With basic respect for Android Framework engineers, this kind of stupid mistake doesn’t make sense. If onInterceptTouchEvent returns true on a DOWN event, onInterceptTouchEvent will not be called on an UP event at all.

On the other hand, if onInterceptTouchEvent returns true on a DOWN event, it means that the control will intercept and process the subsequent event stream, and subsequent event calls will no longer need to be intercepted.

And so it is.

① onInterceptTouchEvent only intercepts DOWN events. Otherwise, FrameLayout will not respond. (2) There is no need to process the UP event. After the DOWN event is intercepted, the onInterceptTouchEvent will not be called at all. {:.success}

Because FrameLayout intercepts the event directly on the DOWN event, TextView doesn’t have a chance to consume the event. But if we go a step further and peek into the event distribution mechanism.

2.2 Only UP Events are Intercepted instead of DOWN Events. Who consumes UP Events? {:.info}

In theory, the DOWN event is consumed by TextView, and the UP event is intercepted by FrameLayout, which is consumed by FrameLayout. But if so, what about the TextView? Have you considered the feelings of the child controls being intercepted? Just like the leader gave me the opportunity, I also put in the work conscientiously, and then suddenly stopped… I’m still in the middle of something…

Obviously, the blocked controls are satisfied, but the blocked controls cannot be left alone, and a mature event distribution mechanism must be able to properly resolve these “civil disputes.”

This brings us to an advanced point — the CANCEL event. These days, anyone who doesn’t know CANCEL can’t claim to be proficient in event distribution (I can’t claim to be, anyway).

When a subclass of ViewGroup overwrites onInterceptTouchEvent to return true after intercepting an event, the child control will receive a CANCEL event telling it that the event stream has stopped if there is any intercepted child control whose header event stream has been consumed by the quilt control. {:.warning}

This is the general logic of event interception, but careful students will notice that it only answers where the CANCEL event goes, and where did it come from? The event that was intercepted, and who consumed it?

For those of you who have read the source code for analyzing events, the answer is as follows:

(1) The intercepted event is converted to a CANCEL event, i.e. Event.setaction (motionEvent.action_cancel), which is passed to the intercepted child control to tell the event stream to CANCEL. The onTouchEvent in the View consumes the CANCEL event and returns true. ② The dispatchTouchEvent and onTouchEvent of the intercepting control will be called for the subsequent event flow. {:.success}

There’s a problem…

2.3 What if a FrameLayout DOWN event is intercepted but not consumed? {:.info}

You keep going on and on and on. Let’s do another problem.

Sharpening the knife does not delay cutting wood, draw two time sequence diagram summary: ▼


Set key monitor & key mobile distribution

  1. Set the button monitor, press in the red area, move to the blue area to lift, whose button monitor will respond?

{:.info}

This question, seems to have never really thought about…

Based on the above keystroke logic, there is no dispute that the DOWN event is consumed by the TextView. The key question is how to handle the first MOVE event that is not in the red area but is in the blue area, and who consumes the final UP event? Too bad for your hair…

What do we do when we press the button and regret it? What I did was to hold my hand, slowly move it out of the button area, and then carefully lift it up without triggering the click as I wanted (I finally calmed down and tactfully at the last moment of payment).

Based on this common sense, the answer to the above question is that neither FrameLayout nor TextView’s listening events are called. Suddenly it occurred to me that my father asked me a joke: who has a beard, male goat or female goat? I certainly haven’t observed a goat’s beard, but the answer to the question has to be counter-common-sense. She goats have beards, I replied loudly, triumphantly. At this time, my father laughed, have a beard…

Anyway, why are listening events never called?

The answer is in the source code, which I published directly:

① Both DOWN and MOVE events in the red area are consumed by TextView. The first MOVE event in the blue area and subsequent MOVE and UP events are still TextView consumption (surprise). If the entire event stream is TextView consumption, then why does it not respond to onClick? The key is that the MOVE event determines whether to cancel the PFLAG_PRESSED status bit based on whether the current coordinate is in the control. The MOVE event in the first blue area marks the pressed status bit as unpressed (don’t smartly think that moving out and back will respond, there is no chance, MOVE can only cancel the pressed state, only DOWN can mark the pressed state). The down status bit is checked for the UP event, and onClick is triggered only if it is down. (3) There will be no CANCEL event during the process, which is the misunderstanding of some students about the CANCEL event. (4) The CANCEL event generates two conditions: the child control has consumed the DOWN event, but the parent control blocks the subsequent event. {:.success}

For those of you wondering, is there an OnLongClickListener?

3.1 Will OnLongClickListener trigger? What is the relationship between OnClickListener and OnLongClickListener? {:.info}

That’s a good question! And THE answer is straight out:

OnLongClickListener is triggered by an UP event, but not immediately. The OnLongClickListener is triggered by a DOWN event. ② Both OnClickListener and OnLongClickListener will be executed at most. The MOVE event determines whether to cancel the down state bit based on whether the current coordinate is in the control and whether to remove the delayed onLongClick. The UP event checks to see if the onLongClick logic has been executed and returns a status bit before onClick is triggered. If the onLongClick listener returns true, onClick will not be fired, otherwise it will. If the onLongClick listener has not been executed, the onClick is delayed before it is triggered. Intercept CANCEL also removes the delay in executing onLongClick. {:.success}

Or the old rules, draw a sequence diagram summary: ▼

conclusion

Inspired by Exploring the Art of Android Development, try a brief review of the event distribution mechanism using pseudocode.

The difficulty of event distribution lies in the flow of a series of events, turning the single point of the independent problem into a multi-point continuous problem, and all controls go this logic, dizzying inevitably muddle muddle.

Pay tribute to The “leadership assignment task” process of Brother Yu Gang to explain the event distribution mechanism, I also try to summarize:

Come a project, the leadership priority “distribute” bottom, ask you to receive? Of course, no one can force you, you can not pick up (this corresponds to the event distribution does not consume the scenario). The consequence is, there’s no rest, you can do it, you only get one chance. So you confidently say to the leader, I will do a good job (this corresponds to the event distribution consumption scenario). Then the human, material and financial resources of the project will flow continuously (one project corresponds to a complete event flow) to you, and everyone will be happy. After a period of time, the leader found that the project is not as good as expected, and asked you for a soul-touching communication. Finally, the leader tells you that I am now in charge of this project (which corresponds to event interception), and you have a good rest for a period of time (this is the CANCEL event you have received). Subsequent resources are constantly allocated to the leader (in response to rerouted event flow after interception), and the leader has no choice but to work overtime (after interception event flow, the leader is responsible for the event flow until the end, whether you do it or not, this is the “project closed loop”). The company calls this “filling in.” “Distribution” and “filling” are the basic qualities of leadership.

Boring, your friend might say. Now seems to understand, after two days can only pretend to understand, after a period of time may forget all. Is there a model or picture that is easy to remember that reminds us of a better life? I thought about this question but couldn’t find an answer, so I tried a challenge.

Looking at the event distribution process, it’s a bit like fishing:

The first step is to put DOWN the bait and wait for the fish to take the bait (DOWN event distribution), and finally find a U-shaped path, kind of like fishing hook, which is also the origin of the model name.

The second step is to retrieve the fishline (MOVE/UP/CANCEL event distribution). It is not important whether it is consumed (onTouchEvent return value), but whether it is intercepted (onInterceptTouchEvent). The intercepting can only be on the current U-shaped path. And the control’s interception method is not called after interception. This process is a bit like pulling the line straight back. Normally the fish is yours, but the surprise is that a big fish uses the fish on your hook as bait. You get rich because you catch the bigger fish.

Think about it. If you catch the u-shaped path that looks like a fishhook, you can answer all kinds of questions about event distribution. The logical details are shown as follows: ▼

Quoting Liu Zongyuan’s “River Snow” as the end of this paper: A man in a boat suoliweng, fishing alone cold river snow.