Does event distribution really have to start with an Activity?

preface

Nice to meet you

Event distribution is an old topic in Android. Some time ago, I went to an interview with an enterprise, and there was a question about the process of event distribution. The correct answer was to choose: Activity->window-> View. The basic process we all know is to start from Activity distribution.

After I chose, I started thinking, how did that event get to the Activity? For those of you who are familiar with the Window mechanism, event distribution is part of the Window mechanism, and activities are not part of the Window mechanism, so touch events should start from the window.

With these questions in mind, I relearned event distribution and gained a new understanding of event distribution based on the previous content of Window mechanism. This article is intended to answer the question of how events arrive at activities.

You think I’m going to start talking about source code, the underlying system? No, no, no. That’s not what this article is about. What we want to explore is the overall flow of a touch message from the bottom of the system to the Activity for distribution step by step. The logic at the bottom of the system is beyond the scope of this article.

This article is the first in a series of articles on Android touch events. The main content is to analyze the path of touch events. Instead of getting tangled up in the source code and the underlying layer, the general flow of the source of the touch event is presented for a more complete understanding of the event distribution system.

Management unit: Window

Android view management is based on Windows, each window corresponds to a view tree. Management involves drawing views and event distribution. The Window mechanism not only manages the display of the view, but also handles the event distribution of the view. For details on the nature of Windows, see my article on the Window mechanism. To investigate the source of event distribution, you must have some understanding of the Window mechanism.

So, first, understand a concept: View trees.

Our application layout is usually nested with multiple layers of viewgroups and views, as shown below:

Their corresponding structural relations are shown in the figure below

At this point, we can call the layout a view tree rooted in a LinearLayout. LinearLayout can directly access FrameLayout and RelativeLayout, because they are both child Views of the LinearLayout. The same LinearLayout can directly access Button.

Each view tree has a root called ViewRootImpl, which is responsible for the entire view tree’s drawing, event distribution, and so on.

Our application interface usually has multiple view trees. Our activity layout is a view tree, the suspension window of other applications is a view tree, the dialog interface is a view tree, the view we add using windowManager is a view tree, and so on. The simplest view tree can have only one view.

View drawing and event distribution in Android are based on the View tree. Each view tree is a window. System service WindowManagerService, the display of the management interface is based on the unit of window, or the unit of view tree. View trees are managed by View Tree, so WMS (short for Windows ManagerService) manages view Tree. The diagram below:

  • The WMS runs in the system server process and is responsible for managing all applications. Applications must communicate with WMS across processes through a Binder.
  • Each viewRootImpl has a windowState corresponding in WMS, WMS can find the corresponding viewRootImpl by windowState for management.

An important reason to understand the Window mechanism is that event distribution is not driven by the Activity, but by the system service that drives viewrotimpl, or even, at the framework level, has nothing to do with the Activity. This will help us understand the nature of event distribution.

So how does touch information step by step get to view wrootimPL? Why is viewRootImpl the starting point for event distribution? How does viewRootImpl distribute touch information? That’s what we’re going to talk about.

How does touch information get to the view wrootimPL?

We all know that touch information is generated when we touch the screen with our fingers. This touch information is generated by the hardware on the screen, picked up by the underlying driver of the system, and delivered to the Android input system service: InputManagerService, also known as IMS.

IMS processes this touch information, finds the window to distribute through WMS, and then sends it to the corresponding View wrootimPL. So it’s not the WMS that sends the touch message, it’s the WMS that provides the information about the Window.

This section deals with the underlying logic of the system and is not the focus of this article. Interested readers are recommended to read Gityuan’s article Input System – Event processing process. I’m not going to explain it here. The general process is shown as follows:

When the viewRootImpl receives the touch message, it is the beginning of the application process event distribution.

How does viewRootImpl distribute events?

As we mentioned earlier, the View view PL manages a view tree. The outermost layer of the view tree is the viewGroup, and the viewGroup inherits from the View. So the whole view tree can be viewed externally as a view. The viewRootImpl receives the touch information, processes it, encapsulates it as a MotionEvent object and sends it to the view it manages for distribution.

As mentioned earlier, the root node of the view tree can be a viewGroup or a single view, so there are two different ways to distribute: directly to the view or to the viewGroup for event distribution. The viewGroup inherits from the View, which has a method for distributing events: dispatchTouchEvent. Subclasses can override this method to implement their own distribution logic, and ViewGroup overrides this method.

In our application layout interface or dialog layout interface, the top-level viewGroup is a DecorView, so the dispatchTouchEvent method of the DecorView is called for distribution. The DecorView overrides the method with simple logic and just makes a judgment:

DecorView.java api29
public boolean dispatchTouchEvent(MotionEvent ev) {
    final Window.Callback cb = mWindow.getCallback();
    returncb ! =null && !mWindow.isDestroyed() && mFeatureId < 0
            ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
Copy the code
  1. If the Window callBack object is not empty, the distribution method of the callBack object is called to distribute
  2. If the Window callBack object is empty, the event distribution method of the parent ViewGroup is called for distribution

The windowCallBack is an interface that contains callback methods for window changes, including dispatchTouchEvent, which is the event distribution method.

The Activity implements the window. CallBack interface and sets itself to the DecorView when creating the layout, so that the DecorView dispatches events to the Activity for processing in its layout interface. Similarly, in the Dialog layout, it is distributed to a Dialog that implements the callBack interface.

If the top-level viewGroup is not a DecorView, then the dispatchTouchEvent method that calls the corresponding view is distributed. For example, if the top-level view is a Button, the Button’s dispatchTouchEvent method is called directly. If the top-level viewGroup subclass does not override the dispatchTouchEvent method, then the viewGroup default dispatchTouchEvent method is called directly.

The overall process is shown as follows:

  1. The viewRootImpl will call the managed view directlydispatchTouchEventMethod, depending on the type of the specific view, calls the specific method.
  2. The root view of the view tree can be either a view or a viewGroup, which handles events directly, while the viewGroup distributes them.
  3. DecorView rewrite thedispatchTouchEventMethod, which checks for the existence of a callBack and calls the callBack method first, passing the event to the Activity.
  4. Other viewGroup subclasses distribute events according to their own logic.

Therefore, do touch events have to start with the Activity? No, an Activity is just one of those cases. Only the view tree that the Activity is responsible for must reach the Activity, while other Windows will not pass through the Activity. Touch events start with a viewRootImpl, not an Activity.

Control for the distribution of events

At this point, we know that touch events are sent to the viewRootImpl and then the viewRootImpl calls the methods of its managed View for event distribution. As a normal process, views are distributed down the control tree. Instead, the event goes to the activity/dialog because of the “traitor” DecorView.

As mentioned earlier, a DecorView differs from other viewGroups in that it has a windowCallBack that sends touch events to the callBack in preference, causing the touch events to be removed from the control tree. So how do these callbacks handle touch events? How do touch events get distributed back into the control tree again?

Before you get into the specifics of distribution, you need to understand one class: PhoneWindow.

PhoneWindow inherits from the abstract class Window, but it is not a Window itself, but a Window helper class. We know that a view tree, or control tree, is a window. PhoneWindow internally maintains a tree of controls and some window parameters. The root view of the control tree is called a DecorView. Their relationship to the Activity is shown below:

Our Activity manages the tree of controls by holding the PhoneWindow instance directly. A DecorView can be thought of as a template interface. Its layout looks something like this:

Our Activity layout is added to the interior column and is part of the DecorView control tree. In this way, an Activity can indirectly manage its own interface through PhoneWindow, hosting window-related operations to PhoneWindow and reducing its own burden.

PhoneWindow is not exclusive to activities; others, such as Dialog, create a PhoneWindow of their own. PhoneWindow is simply a window helper class that helps controls create and manage interfaces better.

When the DecorView receives an event, it calls a windowCallBack method to distribute the event. Let’s look at how the Activity is distributed:

Activity

We’ll start with the Activity’s implementation of the callBack interface method:

Activity.java api29
public boolean dispatchTouchEvent(MotionEvent ev) {
    // The down event calls back to the onUserInteraction method
    // This method is an empty implementation for developers to override
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    // getWindow returns the PhoneWindow instance
    // Call the PhoneWindow method directly
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    // If the event was not handled during the previous distribution, call the Activity's own methods to handle the event
    return onTouchEvent(ev);
}
Copy the code

As you can see, the event distribution logic of the Activity is relatively simple, calling the PhoneWindow method directly to distribute the event. If the event is not handled, handle the event yourself. Let’s see how PhoneWindow handles this:

PhoneWindow.java api29
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}
Copy the code

The mDecor in this case is the DecorView maintained inside PhoneWindow. The method to see a DecorView is:

DecorView.java api29
public boolean superDispatchTouchEvent(MotionEvent event) {
    return super.dispatchTouchEvent(event);
}
Copy the code

Well, the DecorView doesn’t do anything about the event, either, and just calls the parent class’s method to distribute it. DecorView inherits from FrameLayout, but FrameLayout does not override the dispatchTouchEvent method, so it calls the viewGroup method instead. So at this point, events are handed over to the viewGroup to distribute to the control tree.

Let’s review: The Activity goes directly to the PhoneWindow, the PhoneWindow goes directly to its internal DecorView, and the DecorView calls the methods of its parent ViewGroup. Viewgroups are distributed logically to the child controls of interest throughout the control tree. How viewGroups are distributed will be covered in a future article.

Starting from the DecorView, we circle back to the control tree for distribution. Here’s how Dialog is distributed:

Dialog

Look directly at the diapatchTouchEvent code in the Dialog:

Dialog.java api29
public boolean dispatchTouchEvent(@NonNull MotionEvent ev) {
    if (mWindow.superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}
Copy the code

In this case, mWindow is the PhoneWindow instance maintained internally by Dialog, and the following logic is the same as the flow of the Activity, which is not described here.

If you don’t use a DecorView as a template window, the process would be different, such as PopupWindow:

PopupWindow

PopupWindow’s root View is PopupDecorView, not DecorView. Although his name comes with DecorView, it has nothing to do with DecorView, and is directly descended from FrameLayout. We see his event distribution method:

PopupWindow.java api29
public boolean dispatchTouchEvent(MotionEvent ev) {
    if(mTouchInterceptor ! =null && mTouchInterceptor.onTouch(this, ev)) {
        return true;
    }
    return super.dispatchTouchEvent(ev);
}
Copy the code

MTouchInterceptor is an interceptor that we can manually set to PopupWindow. Time is given to the interceptor first, and if there is no interceptor or interceptor does not consume the event, then it is given to the viewGroup for distribution.

conclusion

Finally, let’s review the entire process:

  1. After receiving an event from the underlying system, the IMS obtains the Window information from the WMS and sends the event information to the corresponding View WrootimPL
  2. The viewRootImpl receives the event information, encapsulates it as a motionEvent object, and sends it to the managed View
  3. The view will distribute the event or process it itself, depending on its type
  4. The top-level viewGroup is usually a DecorView, which, depending on its own callBack, chooses to call either the callBack or the methods of its parent viewGroup
  5. Regardless of the type of the top-level viewGroup, it will eventually reach the viewGroup for event distribution.

Here, although we are not clear about the “pulse” of touch events, but his “coming dragon” has been very clear.

The main content of this article is about the source of the event, but the source of the event distribution is far from so simple, the details of the source code has a lot of content worth us to learn, and this article is just the overall process out. For more information on the underlying system, I recommend a series of books for those interested: “Understanding Android ⅰ ⅱ ⅲ”.

The next article will focus on how a viewGroup distributes events to its child views without error.

Thanks for reading.

Full text here, the original is not easy, feel help can like collection comments forward. I have no talent, any ideas welcome to comment area exchange correction. If need to reprint please comment section or private communication.

And welcome to my blog: Portal