android源码学习-事件分发处理机制

首先来一张图镇楼,说明一下方法的依次调用顺序:

android源码学习-事件分发处理机制_第1张图片

这张图囊括了,从native回调java,一直到ViewGroup处理的所有方法堆栈调用。

我把事件分发分为4个部分:

第一部分:InputEventReceiver

1、当用户点击了屏幕上的某个位置之后,native层会接收到。会通过方法回调通知java层这个点击事件。方法位于InputEventReceiver类中的dispathInputEvent方法。

 

// Called from native code.
@SuppressWarnings("unused")
private void dispatchInputEvent(int seq, InputEvent event) {
    mSeqMap.put(event.getSequenceNumber(), seq);
    onInputEvent(event);
}

我们可以清楚的看到,这个调用是来自于native层。而我们的代码研究,也就只从java层开始。

 

这里传入的InputEvent的类型其实是MotionEvent,比较重要的属性会有下面这些:

action//类型,比如ACTION_DOWN、ACTION_UP、ACTION_MOVE等等。

x[0]//X轴坐标

y[0]//y轴坐标

toolType[0]//触发类型,目前有TOOL_TYPE_FINGER手指点击触发、TOOL_TYPE_STYLUS手写板触发、TOOL_TYPE_MOUSE鼠标触发、TOOL_TYPE_UNKNOWN未知四种。PS:都给鼠标和手写板准备上了,看来以后真的是准备把android做到PC上了。

eventTime//点击时间

mSeq//对列号。这个参数还有搞太清楚,推测是序列号,一次性N多事件触发时,按照这个顺序来逐个处理的吧。

2、ViewRoomImpl类当中,会有一个类WindowInputEventReceiver,这个类继承自InputEventReceiver。在ViewRoomImpl类setView的时候,初始化WindowInputEventReceiver。dispathInputEvent方法中会调用onInputEvent方法,而onInputEvent这个方法被子类重写掉了。WindowInputEventReceiver中的onInputEvent的实现方法如下:

 

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

调用enqueueInputEvent实现了向ViewRootImpl层的传递。

 

 

第二部分:ViewRootImpl部分

1、ViewRootImpl中会有一个ViewRootHandler来接收各种传递过来的事件、其中如果MessageType为MSG_DISPATCH_INPUT_EVENT、MSG_SYNTHESIZE_INPUT_EVENT、MSG_DISPATCH_KEY_FROM_IME时,会调用enqueueInputEvent方法。

2.调用ViewRootImpl类中的enqueueInputEvent方法,处理输入事件。

参数为

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

 

其中通过ViewRootHandler调用的processImmediately值必定为true。

3.这里的enqueueInputEvent方法中如果processImmediately的值为false,则是由于其余的补偿逻辑执行调用过来的,最终也会通过发送what值为MSG_PROCESS_INPUT_EVENTS的Message事件,传递给主线程来执行。

4.ViewRootHandler中接收事件,如果waht值为MSG_PROCESS_INPUT_EVENTS,则会调用doProcessInputEvents方法。
5.doProcessInputEvents方法中,处理输入事件是以while链表的方式来去逐个的处理。获取最前端的HeadEvent,然后把这个对象设置给一个方法内变量q。设置HeadEvent为q.next,再把q.next设置为空。最终执行deliverInputEvent(q),处理这个点击事件。也就是说,无论这个事件是否处理成功或者完成,都已经消费掉了,不会再加入到队列当中。(PS:最终处理失败后是否再加回队列的逻辑再说)
6.deliverInputEvent方法中,获取InputStage对象。并调用stage的deliver(QueuedInputEvent)方法

7.普通流程的话InputStage会调用apply方法。

apply(q, onProcess(q));

这里的参数2是以onProcess(QueuedInputEvent)形式传入的。

其实这里的stage是父类,其子类是ViewPostImeInputStage,所以实际上会调用子类的onProcess方法,然后调用processPointerEvent(QueuedInputEvent)方法
8.这里有一行代码如下:
 boolean handled = mView.dispatchPointerEvent(event);
mView指向的就是我们展示层的View对象,从而ViewRoomImpl的使命结束,传递事件给View层处理。

第三部分:Activity层面的部分

1、上面所说的view,实际上指的是ViewRootImpl中的最上层根节点的view,这个view其实就是DecorView,一个界面对应一个window,对应着唯一的一个DecorView。

DecorView中的方法:

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

Window.Callback这个对象就是Activity,可以看到Activity是实现了Window.Callback这个接口的。

所以最终会调用到Activity的dispatchTouchEvent的方法。

2、Activity中包含为一个PhoneWindow,PhoneWindow中包含唯一的一个DecorView,这个就是整个界面的根布局。

DecorView通过superDispatchTouchEvent方法调用父类方法dispatchTouchEvent(),而父类就是ViewGroup。

 

第四部分:View以及ViewGroup层面的部分

1、dispatchPointerEvent方法中,判断是否是正常的touch事件,如果是则调用dispatchTouchEvent方法。(android26以后改了,直接跳过了dispatchPointerEvent方法)
2、对于dispatchTouchEvent方法,View和ViewGroup的实现逻辑就不一致了。
对于View来说,经过简单的判断逻辑后,直接调用View类中的onTouchEvent(MotionEvent)方法来处理输入事件。
3、对于ViewGroup来说,首先判断
intercepted = onInterceptTouchEvent(ev);//获取是否打断事件传递,如果没有并且没有取消该事件。
需要for循环其内部的mChildren子View队列,顺序是从后向前遍历。
 for (int i = childrenCount - 1; i >= 0; i--) {//实现逻辑}
for循环当中,会调用dispatchTransformedTouchEvent方法,该方法中,会调用子View的dispatchTouchEvent方法。实现递归逐级调用。如果子View中没有处理完成,则会根据当前child生成newTouchTarget,把添加到队列mFirstTouchTarget的最前面。

然后根据TouchTarget判断队列中其他的是否处理完成。

 

4、onTouchEvent方法。ViewGroup逐级下发,最终会传递到最终的View控件的onTouchEvent方法当中。
这里面,会根据状态的不同设置不同的状态。比如setPressed这样的状态。
其次,如果是ACTION_UP事件的话,会执行下面的代码段

  if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    performClick();
                                }

 

 

如果没有执行过,则执行performClick方法。而performClick方法会最终会回掉mOnClickListener.OnClick(this),这也就是我们最经常使用的setOnClickListener的回调来源。

 

 

 

5、如果你还想问其它的回调怎么处理的。View方法中搜一搜就知道了。

比如mOnTouchListener,就是在dispatchTouchEvent方法中执行的,如果onTouch返回true,那么后面的onTouchEvent方法就不会执行。

代码如下:

 ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }


            if (!result && onTouchEvent(event)) {
                result = true;
            }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(安卓源码探究)