Android bottom exploration: design and implementation of Android event interception mechanism

Preface

I read an article not long ago, which said " I don't recommend that you work too hard! ". After reading it, it feels very reasonable-the Android learning path, if you don't find the right direction and method of learning, it will be really thankless to learn.

Recently, I myself have been doing systematic data sorting. This is a relatively good article I found in the process of sorting. Now I share it with you, I hope it will be helpful to you.

Overview

Complete mastery of Androidthe event distribution system is not easy, the entire process involved in the system boot process ( SystemServer), input management ( InputManager), system services, and communication of the UI ( ViewRootImpl+ Window+ WindowManagerService), Viewlevel event distribution mechanism and so on a series of links.

Event interception mechanism is based on the Viewlevel of the event distribution mechanism to a point of advanced sexual knowledge, this article will explain more careful of them.

Event interceptor mechanism itself is relatively independent, so this does not require readers to the event distribution mechanism prior knowledge related to which the interested reader can refer to the following information:

Reflection | Design and Implementation of Android Event Distribution Mechanism

The overall structure of this article is as follows:

Speaking from the sequence of events

1. What is a sequence of events

Want to say clearing event distribution mechanism and event interception mechanism , the sequence of events is the first to understand the concept.

What is a sequence of events? GoogleThe official described the document as its The duration of the touchname suggests, we can be understood as the user a full touch operation process - for example, the user clicks the button, the user slides the screen, users press the screen in a UI element and so on, All belong to this category.

2. Reason

Why sequence of events is a very important concept?

In the previous article , readers have already understood that the essential principle of event distribution is recursion. The simple way to implement this is: each time a new event is received, a recursion is required to find the corresponding consumption event View, and return to the event distribution in turn. the result of.

With each touch event as the most basic unit, Viewtraverse the tree once recursively? The impact of this on performance is obvious, so this design has room for improvement.

How to improve this problem? The sequence of events as the most basic unit for processing is more appropriate.

First, the designer MotionEventadds an Actionattribute to describe the behavior of the event according to the user 's behavior:

  • ACTION_DOWN: The act of touching the screen with a finger
  • ACTION_MOVE: The act of moving a finger on the screen
  • ACTION_UP: The act of leaving the screen with a finger
  • …Other Actions, such as ACTION_CANCEL

We know, for one-touch operation of the user, must correspond to a sequence of events , from the user's finger touches the screen to move your finger, and then lift your finger - must contain a single sequence of events ACTION_DOWN, ACTION_MOVE... ACTION_MOVE, ACTION_UPand many other events, which ACTION_MOVEare The number is uncertain, ACTION_DOWNand ACTION_UPthe number of sums is 1.

Familiar with the sequence of events concepts, designers can begin to design and improve existing code, that idea is as follows: when one received ACTION_DOWN, the start means a complete sequence of events, through recursive traversal to find the real consumption of the event Child, And save it . When the ACTION_MOVEsum ACTION_UPbehavior is received later , the traversal recursion process is skipped and the event is directly distributed to the corresponding consumer:

Thus, the sequence of events in the event distribution of knowledge is indeed a very important core concepts (or no one), the most important is adequate saving performance : a normal touch user behavior, that sequence of events includes a number of A touch event, these events do not use recursive algorithms to find event consumers every time, because this consumes a lot of memory- when the sequence of events is more complex, or the Viewtree is nested deeper, this advantage becomes more and more obvious.

So, how does the designer of the source code ensure that Viewthe child corresponding to the event consumer in the tree is found through a recursive algorithm View, and what is its data structure?

If you think about it carefully, it is not difficult for readers to come to the answer: linked list .

Why is a linked list

Why use a linked list, and is there a simpler and more rude implementation solution?

Of course, the most intuitive way to achieve this seems to be: after the first event distribution is completed through recursion, the consumer of the event is saved as a member in the current parent View:

Admittedly, such a design can achieve our desired effect, but the reader to think carefully about that, this is the biggest design problem destroyed the tree structure of internal autonomy .

Is it reasonable for the top level to Viewdirectly hold a Viewreference from the bottom level ? the answer is negative. First of all, this leads to Viewconfusion among hierarchical dependencies; second, the top layer Viewitself holds a Viewreference to one of the lowest layers , and Viewthe targetattributes of several levels in between are meaningless.

A more incisive way to apply the tree structure is to build a linked list:

Each Viewnode holds the next-level consumer of the event. When the subsequent touch event of the same event sequence arrives, it is no longer necessary to perform performance-consuming DFSalgorithms, but directly handed over to the next-level child View, and the child Viewis directly delivered to the next-level child . The next level of children Viewuntil the event reaches the real consumer:

Similar to the definition of a linked list, the designer designs a TouchTargetclass and ViewGroupdeclares such a member for each one as a node of the linked list to describe the transmission direction of the current sequence of events:

public abstract class ViewGroup extends View {

  // 链表的下一级结点
  private TouchTarget mFirstTouchTarget;

  private static final class TouchTarget {
      // 描述接下来的触摸事件由哪一个子View接收并分发
      public View child;
  }
}

So how is this linked list constructed? As mentioned above, when one ACTION_DOWNis received , it means the beginning of a complete sequence of events. Recursive traversal is used to find the one that actually consumes the event.Child

Readers need to carefully try to figure out the sequence of events related concepts, because this knowledge throughout the entire distribution mechanism event flow, can be said to be the very core of knowledge; at the same time, it is also below grasp grasp the event interception mechanism critical.

Event interception mechanism

Most Androiddevelopers of events to intercept mechanisms are not unfamiliar, the reader should have understood that the ViewGrouplevel of additional design onInterceptTouchEvent()functions and exposed to outside developers so as to achieve ViewGroupnot touch event to Viewhandle, but their own decision whether to consume Events, and feed back the results to the higher level ViewGroup.

1. Reason

Why is such an interception mechanism designed? In fact, this is necessary. Taking the conventional ScrollViewcorresponding sliding page as an example, when the user throws a list sliding operation, is the corresponding touch event sequence still necessary to be handed over to ScrollViewthe child Viewfor processing?

The answer is no. When ScrollViewa sliding operation is received, of course, the events related to this sliding operation no longer need to be handed over to the child View, but directly to ScrollViewhandle the sliding operation.

Readers also need to understand that not all event sequences will be intercepted -when the user clicks ScrollViewon a certain button, the designer expects that the series of events corresponding to this click operation can be ScrollViewdistributed to the child Buttonfor processing, so that the developer can eventually OnClickListenerObserve this click event in the button itself , and perform the corresponding business operation.

Therefore, for different types of ViewGroupdevelopers needs in different scenarios, decide whether to intercept the event, which the parent control according to their own responsibilities to intercept the sequence of events specified scene behavior, which we call the event interception mechanism .

2. Intercept function: onInterceptTouchEvent()

So developers how to do, to ensure that events of different scenarios are reasonably distributed directly down or intercept it? Accordingly designer provides onInterceptTouchEvent()interception function:

public abstract class ViewGroup extends View {

  public boolean onInterceptTouchEvent(MotionEvent ev) {
      // ...
      return false;
  }
}

The definition is that when a touch event comes, the event is first passed into the onInterceptTouchEventfunction as a parameter , and the developer customizes the onInterceptTouchEventinternal logic to decide whether to intercept the event and return the booleantype of result. When the return value trueis, all subsequent events of the event sequence will be ViewGroupintercepted by the current ; usually, ViewGroupthe function returns falseby default , that is, the event is not intercepted.

Text above example, we can ScrollViewadd something like the following strategy - when a user initiates a click event when the operation onInterceptTouchEventreturns false, the event will give the child control downstream to decide whether or not the consumer; and when the user slides the screen , the event will Sequence for interception:

public class ScrollView extends ViewGroup {

  public boolean onInterceptTouchEvent(MotionEvent ev) {
      // 这里模拟一个抽象的函数代替实际的业务逻辑
      // 实际源码中,这里是根据对触摸事件序列的复杂判断,得出操作是否是滑动事件
      if (isUserScrollAction(ev)) {
        return true;
      } else {
        return false;
      }
  }
}

The sequence of events in the process once again played a crucial role. For a single touch event - for example, ACTION_DOWNor ACTION_MOVE, we can not determine whether this is we want to intercept the operation. And when we get to the sequence of events after the number of consecutive events, we can according to the direction and distance of the gesture operation (to determine whether the slide), time of the touch screen (judge or long click event by event) to the user The behavior is defined and the final decision is made whether to intercept.

- This means that, when ScrollViewreceiving the initial ACTION_DOWNevent, the parent control did not immediately intercept the incident, but to the child Buttonto the consumer; and when receiving a number of ACTION_MOVEevents, ScrollViewthe onInterceptTouchEvent()function determination has been made of this The direction of the touch behavior is downward, which is a sliding event , and then the function returns true, causing the current and subsequent touch events to be intercepted.

and many more! At this point, the reader seems to have inferred a weird conclusion: for the downward distribution process of a complete sequence of events, the consumer of the touch event does not necessarily have only one role -this does not seem to be intuitive.

But it is true.

Since a complete sequence of events that the event might give a different role, does this mean that in extreme cases, a user's sliding behavior will not only trigger the parent control itself sliding effect, users will also received Buttonchild control click effect?

This defect does exist in the design so far, so next we need to add new logic units to make up for this problem, and make a ACTION_CANCELshining debut.

3. ACTION_CANCEL: make up and end

Finally we came to the ACTION_CANCELstage, the announcer introduced this actor is two words: make up and end .

Now we hope that when Buttonthe parent control ScrollViewintercepts the sliding operation, Buttonthe click event will no longer be responded to.

In normal logic processing, it is Buttonnecessary to ACTION_UPjudge the duration of the entire event sequence when it is received . If it meets the pre-definition of a series of click operations (such as touchable = trueor clickable = trueetc.), it is directly handed over to the listener of the click event for View.OnClickListenerprocessing .

We can be ACTION_UPregarded as a sequence of events in the termination event , it is clear that this logic in the event interception mechanism is not applicable because when the parent control of the event was to intercept, then the whole sequence all events are forwarded to the The parent control and child control can no longer receive any events, including ACTION_UP.

We always want to stay the course (for example, look forward to the results of the interview timely feedback), event distribution mechanism is also the same, when the sub-control events are intercepted parent control, child controls also need a termination event notifications to make the corresponding behavior.

Therefore, the designer provides an additional ACTION_CANCELevent to notify the current Viewmade to the event intercepted finishing after such cancellation or long-click event by event-related judgment logic timer (if any, the same below), or to The resetting of the calculation of the sliding distance of the current control, etc., avoids the embarrassing scene of "the parent control sliding occurs" and the "triggering child control click".

Now, when the parent control intercepts the touch event, the child control immediately receives an additional ACTION_CANCELcompensation, and hastily carried out related finishing work, and then all the business logic is handed over to the parent control for processing.

Event interception mechanism seems to come to an end at this point. Readers think carefully, is this kind of logical processing perfect at present?

Interception mechanism and anti-interception mechanism

Parent control: "I invalidate your effect." Child control: "I invalidate your effect."

1. Oppression and resistance

The progress bar control of the music player SeekBarmade a serious protest.

As SeekBarwith ScrollViewthe use of matching, the former shocked to discover that, as a child control, which is most proud of skills - slide to adjust the audio function is completely destroyed and progress.

Of course, ScrollViewwhen a sliding event is received, it will naturally intercept all subsequent related events, and the sub-controls ca SeekBarn't even drink the soup.

This is an unreasonable design. The power of the parent control is too great, and the child control is completely helpless. So designers to ViewGroupdesign another one API- requestDisallowInterceptTouchEvent(boolean).

The role of this function is specified by the command ViewGroupwhether or not to intercept for the sequence of events , but a normal event to whether the child controls to deal with consumer events.

As SeekBaran example, it can be designed like this:

public abstract class AbsSeekBar extends ProgressBar {

   // ...代码大幅简化,具体逻辑请参考源码...
   @Override
   public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
          // 当接收到ACTION_DOWN事件时,命令父控件不能拦截事件序列
          case MotionEvent.ACTION_DOWN:
              mParent.requestDisallowInterceptTouchEvent(true);
              break;
          // ...
        }
   }
}

Now, even ScrollViewinside the hold of a slide operation related to the interception mechanism, but SeekBarstill can be a higher level of APIconduct of their repression, thus skips interception parent control and own consumption slip event - the end-user got his wish to get The operating experience (slide to adjust the playback progress).

2. Deeper thinking

The narrative in the previous section is inherently flawed. Generally speaking, the SeekBarprocessing of adjusting the progress is horizontal sliding, and the ScrollViewprocessing is vertical sliding. In essence, the two logics do not conflict.

Describes, just to make it easier for readers to understand the anti interception mechanism corresponding to the requestDisallowInterceptTouchEvent()purpose and significance of the design function, the reader does not have to go into this - of course, the reader may be a custom implementation of a lateral slide HorizontalScrollView, in the previous section to obtain The effect of sliding conflicts will not be repeated in this article.

Another point to think about is, after the child control calls the requestDisallowInterceptTouchEvent(true)function of the parent control to invalidate the interception mechanism of the parent control, does the invalidation of the interception mechanism of the parent control need to always exist ?

The answer is no, is the right way at some point in time should be the parent interception mechanism controls the restart - that call requestDisallowInterceptTouchEvent(false), so as to ensure that when touched to the other sub-control, parent control is still able to events interception mechanism for normal operation .

So how to grasp the time point of this reset ACTION_UP, call it when the child control is received ?

In the child control termination event sequence of events in the reset state , this sounds good, but it needs to be noted that the interception mechanism is invalidated state is the presence of the parent control ViewGroupof, and therefore a change in thinking, the better chance when not actually Is it hidden in ViewGroupit?

3. Better timing

Designers will eventually reset the time on the parent control start event sequence of events - ACTION_DOWNthe processing logic.

public abstract class ViewGroup extends View {

  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
      // ...
      if (actionMasked == MotionEvent.ACTION_DOWN) {
         // 1.这个函数内部将事件拦截功能的开关进行了重置
         resetTouchState();
      }
      // ...2.继续处理事件拦截和事件分发
  }
}

This is indeed a better time to reset the interception mechanism, which not only ensures that the touch events of other sub-controls will not be affected by the previous anti-interception mechanism, but also maintains the ViewGroupautonomy of the internal itself.

This also proves that the sequence of events starting event ACTION_DOWNcan always be received parent control to intercept and process, therefore, can not in most cases the developers ViewGroupof onInterceptTouchEvent()the directly to the ACTION_DOWNevent returns true, as this will cause the parent control to intercept the entire sequence of events , even the child controls ACTION_DOWNare not acceptable, anti-interception mechanism completely ineffective.

to sum up

Event interception mechanism is a very important basic knowledge, but the sequence of events is one of the most central concepts, regardless of the event distribution or event interception , get to know the sequence of events meaning, understanding other logical concepts are no longer difficult.

Reference & additional words

Can this article help me understand the Android event interception mechanism?

Of course not. In the process of writing this article, the author finally deleted some more detailed knowledge points, such as:

  • After the parent control intercepts the event, mFirstTouchTargetwhat happens to its internal changes? (Update operation of event delivery linked list)
  • What problems are often used to solve the problem of development in the event interception mechanism? (Resolve sliding conflict)

Etc. These details are just as important, they are filled event interception mechanism complete system of blood and flesh, the reader is advised herein in conjunction with the following relevant information to open a more detailed exploration trip.

Author: yet the Qing Mei sniffing
the original address: https: //juejin.cn/post/6844904128397705230

At last

I put the most important and popular learning direction materials for Android that I have compiled during this time on my GitHub: https://github.com/xieyuliang/Android Click here to see the blue font , and there are self-study in different directions. Programming route, interview question collection/face-to-face, and series of technical articles, etc.

The resources are continuously updated, and everyone is welcome to learn and discuss together.

Guess you like

Origin blog.csdn.net/BUGgogogo/article/details/113180928