首先来一张图镇楼,说明一下方法的依次调用顺序:
文章图片
这张图囊括了,从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轴坐标
【android源码学习-事件分发处理机制】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;
}
推荐阅读
- android源码学习-activity启动流程(android8.0源码)
- android源码学习-View绘制流程
- 安卓源码探究|android源码学习-目录
- 安卓源码探究|android源码学习-Handler机制及其六个核心点