A Preliminary Study on the Source Code of Android Event Distribution Mechanism Based on Hardware

Posted by bundred on Sat, 01 Jun 2019 20:25:05 +0200

First of all, I read a lot of articles on the Internet, including Guo Linda's articles. They all analyzed the event distribution of ViewGroup and View perfectly. But still confused: how did the Touch Event ViewGroup capture it? The gods all said that Activity,Window,ViewRoot and so on were all involved in the event distribution. How did they participate? Who was the first to receive touch events? I am very puzzled, so bite check data analysis source code to learn a wave, then let's explore unexpectedly! Because it is detailed and comprehensive, the length of the article is slightly longer. So it is divided into two parts. This is the first half of this article, which is dedicated to analyzing the process of events passing from the source of mobile phone hardware to the layout we wrote ourselves. The source code of this article is from API24.

First of all, I would like to summarize the attached flowchart to give an early overview of the play.

When a touch event occurs, the mobile phone hardware monitors the event and hands it to ViewRootImpl. Then ViewRootImpl hands it to DecorView. Then DecorView hands it to Activity through Windows. Then Activity hands it to Window and Windows to DecorView. Then DecorView begins to give it to the layout we defined.

(Receiver,Handler,Stage on the left of the figure will be explained below)

Next, let's analyze it carefully.

When we touch the screen with our finger, the hardware of the mobile phone will process it accordingly and then send out a notification. In the source code, there is a receiver called InputEventReceiver. As the name implies, the input event receiver. Where is this thing used? Look, look, haha, and find this stuff in ViewRootImpl!
And what is ViewRootImpl? To illustrate briefly:

It is a link to connect Window s and DecorView, and it also triggers the drawing process of measure, layout and draw to complete View. It also serves to distribute a series of input events to View, such as touch, keyboard events, etc.

In ViewRootImpl, a Windows InputEventReceiver class inherits from InputEventReceiver and overrides the onInputEvent() method:

 final class WindowInputEventReceiver extends InputEventReceiver {
        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
        }

        @Override
        public void onInputEvent(InputEvent event) {
            enqueueInputEvent(event, this, 0, true);
        }

So where is this class instantiated? There is such a code in the setView() method of ViewRoot:

    if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }

It's here that you instantiate, so when ViewRoot adds Windows to Windows Manager (setView() specifically calls the reader to look it up, you can see another Windows related article of mine, which I won't go into any further here) you will create a receiver. When the mobile phone hardware issues InputEvent, the onInputEvent() method of Receiver is invoked, whereas the enqueueInputEvent (event, this, 0, true) is invoked here (see the Windows InputEvent Receiver source code above).

You can see that the enqueueInputEvent() method is called here, inserting input events as the name implies. Let's continue to follow:

  void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {

     ....//Other code

        if (processImmediately) {
            doProcessInputEvents();
        } else {
            scheduleProcessInputEvents();
        }
    }

Continue to post out the key code, first of all, process Immediately is judged, true, call the doProcessInputEvents() method, if false, call the scheduleProcessInputEvents() method, in fact, scheduleProcessInputEvents() internal or finally call the doProcessInputEvents() method, which is not in-depth here, interested comrades can see for themselves.
Let's follow up the doProcessInputEvents() method. :

void doProcessInputEvents() {
        ...//Other code
            deliverInputEvent(q);
        ...//Other code
    }

As a rule, we post key code, call delivery InputEvent () method here, or pass input events as the name implies. We continue to follow up:

    private void deliverInputEvent(QueuedInputEvent q) {
         ....//Other code

        InputStage stage;
        if (q.shouldSendToSynthesizer()) {
            stage = mSyntheticInputStage;
        } else {
            stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
        }
//Code annotation
        if (stage != null) {
            stage.deliver(q);
        } else {
            finishInputEvent(q);
        }
    }

In the above we can see that an InputStage is defined. What is this? Here we take a look at the official annotations:

 /**
     * Base class for implementing a stage in the chain of responsibility
     * for processing input events.
     * <p>
     * Events are delivered to the stage by the {@link #deliver} method.  The stage
     * then has the choice of finishing the event or forwarding it to the next stage.
     * </p>
     */
    abstract class InputStage {

This is the class used to handle a series of input events (such as touch screen, keyboard). Events are passed to this class by calling deliver y method. This class can choose to terminate events, and can choose to process them.

Back to that, the stage.deliver() method was called at the code annotation above. Let's go in and see:

   public final void deliver(QueuedInputEvent q) {
            if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
                forward(q);
            } else if (shouldDropInputEvent(q)) {
                finish(q, false);
            } else {
                apply(q, onProcess(q));
            }
        }

As you can see in the above code, he can choose to continue passing events or to terminate events, and finally he can call an application method, which I understand as representing the handling of events.

When an InputEvent arrives, ViewRootImpl looks for the appropriate InputStage to handle. In ViewRootImpl, several xxxInputStage classes are defined to inherit from the InputStage class, which can be used to handle different events differently.
For click events, ViewPostImeInputStage can handle it, so call the onProcess method in the ViewPostImeInputStage class. When onProcess is called back, at least one method of processKeyEvent, processPointerEvent, processTrackballEvent, and processGenericMotionEvent will be called, all of which belong to ViewPostImeInputStage. The onProgress method is as follows:

  @Override
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            } else {
                final int source = q.mEvent.getSource();
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                    return processPointerEvent(q);
                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                    return processTrackballEvent(q);
                } else {
                    return processGenericMotionEvent(q);
                }
            }
        }

Here we see that different processing methods are invoked depending on the input event. We follow several processxxEvent methods in and find that the mView.dispatchxxxEvent() method is invoked. These dispatchxxEvent () methods are the implementation of the distribution event mechanism.

Well, after so long, I finally scattered flowers and flowers. ... Uh-huh, what's that? What is mView? You're such a fool. What happened for a long time? What happened to Window s? What happened? Well, stop talking, dig your own pit and fill it slowly. Let's go on and see what this mView is. (To be honest, it's disgusting that the variable name is better than the test name.)

The first reaction is to go to the ViewRootImpl construction method to see, the result did not find the assignment, and then we can only keep looking, looking for a while, eh? Find it, haha. In the setView() method of ViewRootImpl, it is found that the source code is first up:

   public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                ...//Other code
            }
              ...//Other code
      }
      ...//Other code
}

The assignment is made here, so what is the incoming view? In fact, this incoming view is generally DecorView, because ViewRoot is the link between Window s and Root View, and Root View is DecorView. As for where to call this method, interested comrades can do it on their own (let's go, I'll write it down here as well). In another record of mine Preliminary Exploration of Window s and DecorView in Android It is mentioned that adding Windows to Windows Manager calls Windows Manager. addView (mDecor, getWindow (). getAttributes ()) in the makeVisible() method of Activity. The source code for the addView() method is as follows (see Windows Manager Impl):

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

We see that the mGlobal.addView() method has been invoked to follow up:

 public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ...//Other code

        ViewRootImpl root;
        View panelParentView = null;
        ...//Other code

        root = new ViewRootImpl(view.getContext(), display);
        ...//Other code
        root.setView(view, wparams, panelParentView);
        ...//Other code
        }
    }

As you can see here, the ViweRootImpl object is instantiated and the setView() method is called.
!!!! See it! I am Cao! At last I see this way! Here the view is passed in, and this view is the incoming DecorView!
So the truth is clear! So far we've seen the role of ViewRoot in event distribution! It acts to capture touch events and then pass them to DecorView.

So let's go back to calling the mView. dispatchxxEvent () method we talked about a long time ago. Then let's go to DecorView to see the dispatchxxEvent () method. Here we take the dispatchTouchEvent() method to analyze, first code:

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

We see that a dispatchTouchEvent() method of Window.Callback is called here. Oh, my God, what's this (to tell you the truth, I've got a good overview of this variable name /(o)/~)
I began to look for it again, Cao, and found that it wasn't found in Windows. Then I thought about it carefully. Windows was newly built in the attach() method of Activity. So what about going to see it?
As a result, Woody, Woodday, I'm so good? Code first:

 final void attach(Context context, ActivityThread aThread,Instrumentation instr, IBinder token, int ident,Application application, Intent intent, ActivityInfo info,CharSequence title, Activity parent, String id,NonConfigurationInstances lastNonConfigurationInstances,Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window) {
        ...//Other code
        mWindow.setCallback(this);
        ...//Other code
    }

We see that CallBack is set here with this parameter, which means the current activity. It suddenly dawned on me that CallBack was Activity. Then the cb.dispatchTouchEvent() method previously called is the Activity method. So let's continue to look at Activity. First we go to Activity's dispatch TouchEvent () source code:

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

First you see the call to the superDispatchTouchEvent() method of window s. Let's follow up PhoneWindow (the only Windows implementation class in Android) to see the source code analysis:

 @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

We can see that the superDispatchTouchEvent() method of DecorView is invoked here, and we continue to follow up.

 public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }

The dispatchTouchEvent() method of the parent class is invoked here, and DecorView is inherited from FrameLayout, that is, from ViewGroup. So dispatchTouchEvent() is the method of ViewGroup.

So far, we have analyzed event distribution from the source to the EcorView of the internal layout of the system. Next, we can say that we start with the layout we write ourselves, and process the distribution events.

Between this article has been too long, don't read, I write dizzy. Then I will write another article to record the specific event distribution of ViewGroup and View.

Finally the end of the flower!!!!

Topics: Windows Mobile Android