Android event distribution mechanism --ViewRootImpl articles (prequel)

Disclaimer: This article is a blogger original article, reproduced welcome but must indicate the source Thank you! https://blog.csdn.net/dongxianfei/article/details/83863888

We all know that the distribution order View events is Activity-> Window-> View.

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

//PhoneWindow
@Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
    	//mDecor即为DecorView
        return mDecor.superDispatchTouchEvent(event);
    }

//DecorView
public boolean superDispatchTouchEvent(MotionEvent event) {
		//调用父类ViewGroup的dispatchTouchEvent进行事件分发
        return super.dispatchTouchEvent(event);
    }

Since DecorView extends FrameLayout, and DrameLayout extends ViewGroup, so the final event distribution from ViewGroup.dispatchTouchEvent start, if the above is not clear jump can refer to the Android window mechanism (a) - Window, PhoneWindow, DecorView understand .

But where Event Source Activity in it? ViewRootImpl answers and have a great relationship.

First of all, the fundamental source of the event from Native layer embedded hardware, and then accept the event will go through InputEventReceiver to ViewRootImpl, and then pass the event to DecorView, and then to the final DecorView Activity, event distribution system for the entire order:
The event delivery order
We from ViewRootImpl method of dispatchInputEvent start.

    public void dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
        SomeArgs args = SomeArgs.obtain();
        args.arg1 = event;
        args.arg2 = receiver;
        Message msg = mHandler.obtainMessage(MSG_DISPATCH_INPUT_EVENT, args);
        msg.setAsynchronous(true);
        mHandler.sendMessage(msg);
    }

There are two parameters, InputEvent and InputEventReceiver:

  • InputEvent: base class for the input event, it has two sub-classes, respectively corresponding to MotionEvent KeyEvent and keyboard input event and a touch screen event;
  • InputEventReceiver: to provide for the application of a low-level mechanism to receive input events, which is used to receive input events, then go to dispatchInputEvent ViewRootImpl distribution.
final class ViewRootHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DISPATCH_INPUT_EVENT: {
                    SomeArgs args = (SomeArgs)msg.obj;
                    InputEvent event = (InputEvent)args.arg1;
                    InputEventReceiver receiver = (InputEventReceiver)args.arg2;
                    enqueueInputEvent(event, receiver, 0, true);
                    args.recycle();
                } break;
            }
        }

After forwarded to the UI thread, the call to enqueueInputEvent:

void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
        adjustInputEventForCompatibility(event);
        //将当前输入事件加入队列中排列等候执行
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
        //输入事件添加进队列后,加入输入事件的默认尾部
        QueuedInputEvent last = mPendingInputEventTail;
        if (last == null) {
            mPendingInputEventHead = q;
            mPendingInputEventTail = q;
        } else {
            last.mNext = q;
            mPendingInputEventTail = q;
        }
        //队列计数
        mPendingInputEventCount += 1;
        ...
        //processImmediately则是判断是同步还是异步,前面我们在handler中调用的,因为是在UI线程,肯定是同步的,所以传递了参数是true,如果是异步,则调用到scheduleProcessInputEvents()
        if (processImmediately) {
            doProcessInputEvents();
        } else {
            scheduleProcessInputEvents();
        }
    }

EnqueueInputEvent can see the current input event to join the queue, QueuedInputEvent the equivalent of a linked list, you can see which member variables next, to the next link member.

private static final class QueuedInputEvent {
        public QueuedInputEvent mNext;
        public InputEvent mEvent;
        public InputEventReceiver mReceiver;
}

The obtainQueuedInputEvent is to build a linked list structure for the current input event, and then link to the tail of the queue before.

private QueuedInputEvent obtainQueuedInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags) {
        QueuedInputEvent q = mQueuedInputEventPool;
        if (q != null) {
            mQueuedInputEventPoolSize -= 1;
            mQueuedInputEventPool = q.mNext;
            q.mNext = null;
        } else {
            q = new QueuedInputEvent();
        }

        q.mEvent = event;
        q.mReceiver = receiver;
        q.mFlags = flags;
        return q;
    }

Then we continue to analyze:

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

processImmediately is judged synchronous or asynchronous, in front of our call handler because the UI thread is definitely synchronized, so the argument passed is true, if it is asynchronous, the call to scheduleProcessInputEvents ().

private void scheduleProcessInputEvents() {
        if (!mProcessInputEventsScheduled) {
            mProcessInputEventsScheduled = true;
            Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS);
            msg.setAsynchronous(true);
            mHandler.sendMessage(msg);
        }
    }

case MSG_PROCESS_INPUT_EVENTS:
	mProcessInputEventsScheduled = false;
	//最终还是调用doProcessInputEvents()处理
	doProcessInputEvents();
	break;

Eventually call doProcessInputEvents () process:

void doProcessInputEvents() {
        //循环取出队列中的输入事件
        while (mPendingInputEventHead != null) {
            QueuedInputEvent q = mPendingInputEventHead;
            mPendingInputEventHead = q.mNext;
            ...
            mPendingInputEventCount -= 1;
            ...
            //分发处理
            deliverInputEvent(q);
        }

        //处理完所有输入事件,清楚标志
        if (mProcessInputEventsScheduled) {
            mProcessInputEventsScheduled = false;
            mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
        }
    }

You can see that the method is used to obtain the cyclic input event queue, followed by distribution processing deliverInputEvent (q).

private void deliverInputEvent(QueuedInputEvent q) {
        //校验输入事件
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
        }

        InputStage stage;
        if (q.shouldSendToSynthesizer()) {
            stage = mSyntheticInputStage;
        } else {
            stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
        }

        if (stage != null) {
            stage.deliver(q);
        } else {
            finishInputEvent(q);
        }
    }

In ViewRootImpl, there are a series of similar InputStage subclass, it is an abstract class, which deliver method will process an input event, calls finishInputEvent method after processing is complete.

It has many subclasses, corresponding to a specific InputStage, each InputStage can handle certain types of events, such as AsyncInputStage, SyntheticInputStage, NativePostImeInputStage, ViewPreImeInputStage, ViewPostImeInputStage like.

Subclasses implement the abstract methods InputStage, such onProcess, onDeliverToNext, processKeyEvent, processPointerEvent, processTrackballEvent, processGenericMotionEvent, onProcess in different situations, onDeliverToNext the like will be called back.

When a InputEvent arrival, ViewRootImpl will find the right to deal with its InputStage, InputStage will first call to deliver () starts processing.
InputState process flow
The final event distribution process is onProcess methods apply the method in the.

For click event is, InputState subclass ViewPostImeInputStage can handle it, we look ViewPostImeInputStage of onProcess.

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);
		}
	}
}

private int processPointerEvent(QueuedInputEvent q) {
	final MotionEvent event = (MotionEvent)q.mEvent;

	mAttachInfo.mUnbufferedDispatchRequested = false;
	//调用mView.dispatchPointerEvent()
	boolean handled = mView.dispatchPointerEvent(event);
	//...
	return handled ? FINISH_HANDLED : FORWARD;
}

In fact, processKeyEvent, processPointerEvent, processTrackballEvent, processGenericMotionEvent method has a very key phrase code.

View mView;
mView.dispatchKeyEvent(event)//按键事件
mView.dispatchPointerEvent(event)
mView.dispatchTrackballEvent(event)
mView.dispatchGenericMotionEvent(event)

That mView here in the end what is it? If we are not very clear, you can read my previous blog post
Window window mechanism (three) - WindowManager, ViewRootImpl, View understanding
to understand, in fact mView here is our DecorView, because ViewRootImpl add DecorView method by setView to PhoneWindow in.

Since there is no such DecorView several methods MVIEW., So look for the parent class, which eventually can be seen in the figure View.

//View
public final boolean dispatchPointerEvent(MotionEvent event) {
        if (event.isTouchEvent()) {
        	//判断是触摸方法,调用dispatchTouchEvent()
            return dispatchTouchEvent(event);
        } else {
            return dispatchGenericMotionEvent(event);
        }
    }

Due to the presence of this polymorphism, calls to the subclass DecorView.dispatchTouchEvent View.

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

It can be seen DecorView eventually calls cb.dispatchTouchEvent method, then what the Callback that? Callback In fact, this is the current Activity.

//Activity
public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback {

public interface Callback {
        public boolean dispatchKeyEvent(KeyEvent event);
        ...
        public boolean dispatchTouchEvent(MotionEvent event);
   }

//Activity.attach
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) {
        attachBaseContext(context);
 
        mFragments.attachHost(null /*parent*/);
 
        mWindow = new PhoneWindow(this);
        //setCallback
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);

Here, click on the event was introduced to the Activity, after the process is the beginning of the article analyzes the same as you.

to sum up

ViewRootImpl event distribution flow chart
ViewRootImpl event distribution flow chart

The next event in the View and ViewGroup is how to pass, you can refer to the following blog posts Android event distribution mechanism --View (a) .

Guess you like

Origin blog.csdn.net/dongxianfei/article/details/83863888