手把手教你读懂源码,View事件的注册和接收详细剖析

关于Android的Touch事件传递机制,只是知道事件传入Activity后的流程,但是这些事件是如何传递给Activity的一直模糊不清。现在再来好好回顾一遍,顺道整理一点儿东西出来,同时分享给大家。

前面用源码带着大家分享过View的创建和加载,以及View的绘制流程。

手把手教你读懂源码,View的加载流程详细剖析

手把手教你读懂源码,View的绘制流程详细剖析

今天就随着Android源码一起来探寻一番Android中各View的Touch事件到底是怎么注册和接收的,虽然有一些大神做过分享,但是源码比较老旧,而且通过自己研究会掌握的更透彻一些。本文章主要分析Java部分,底层C++部分暂不分析。

Android输入系统的主要工作是读取设备节点中的原始事件,将其加工封装,然后派发给一个特定的窗口以及窗口中的控件,如一个活动状态的Activity界面中的Button。

同时我们知道Android系统是Linux内核的,它的事件处理也是在Linux的基础上完成的,因此我们从Linux 内核往应用这个方向慢慢理清它的大致处理过程。首先来看事件是如何注册的。

1、事件注册

从之前的View加载流程源码分析知道(如果不清楚,建议返回先回顾View的加载流程),在ActivityThread中的handleResumeActivity方法会调用wm.addView()方法,将View添加到mWindowManger上,即WindowManagerImpl类的addView方法,而其又是由WindowManagerGlobal代理的。在WindowManagerGlobal的addView方法中会先创建了一个ViewRootImpl对象,然后调用ViewRootImpl.setView()方法。

《手把手教你读懂源码,View事件的注册和接收详细剖析》

在setView方法中会找到如下代码:

《手把手教你读懂源码,View事件的注册和接收详细剖析》

会发现在requestLayout后创建一个InputChannel对象,然后会调用addToDisplay方法,继续跟踪分析:

《手把手教你读懂源码,View事件的注册和接收详细剖析》

该方法继续调用了WindowManagerService的addWindow方法:

《手把手教你读懂源码,View事件的注册和接收详细剖析》

该方法先创建了一个WindowState对象,然后调用了openInputChannel方法:

《手把手教你读懂源码,View事件的注册和接收详细剖析》

这一段代码主要就是创建一对InputChannel,同时这一对InputChannel中实现了一组全双工管道。InputChannel创建完成后,会将其中一个的native InputChannel 赋值给outInputChannel,也就是对ViewRootImpl端InputChannel对象的初始化,这样随着ViewRootImpl和WindowManagerService两端的InputChannel对象的创建,事件传输系统的管道通信也就建立了起来。后续的具体内容暂时不是分析重点,以后有机会再进行剖析。

按键、触屏等事件是经由WindowManagerService获取,并通过共享内存和管道的方式传递给ViewRootImpl,ViewRootImpl再调用dispatch给Application。当有事件从硬件设备输入时,system_server端在检测到事件发生时,通过管道(pipe)通知ViewRootImpl事件发生,此时ViewRootImpl再去内存中读取这个事件信息。

下面的结构图比较经典,是从网络上找来的,有一部分类对不上最新代码。其中InputManager变成了InputManagerService,ViewRoot变成了ViewRootImpl。

《手把手教你读懂源码,View事件的注册和接收详细剖析》

2、事件接收

通过上面的分析知道在addView时将事件传输系统的管道建立了起来,那么随后当Linux检测到事件发生,会经过层层传递到ViewRootImpl中,就来一起分析一下事件是如何从Native传递给Activity的。

继续来分析ViewRootImpl类的setView方法,

《手把手教你读懂源码,View事件的注册和接收详细剖析》

这段代码先创建一个与当前窗口已经生成的InputChannel相关的接受输入事件的处理对象,最后设置当前各种不同类别输入事件到来时候按对应类型依次分别调用的处理对象。

上面分析的生成两个InputChannel输入事件通道,其中一个转移到当前顶层ViewRootImpl中并生成一个与输入事件通道关联的事件处理mInputEventReceiver对象,随着这条线索继续分析。

《手把手教你读懂源码,View事件的注册和接收详细剖析》

看到WindowInputEventReceiver类继承了InputEventReceiver类:

《手把手教你读懂源码,View事件的注册和接收详细剖析》

注意InputEventReceiver类的dispatchInputEvent方法,当输入事件到来时该方法由native层代码发起调用,然后调用了onInputEvent(event)方法。从前面知道WindowInputEventReceiver类重写了onInputEvent方法,因此事件会传递到enqueueInputEvent方法。

《手把手教你读懂源码,View事件的注册和接收详细剖析》

该方法先获取一个指向当前事件的输入事件队列QueuedInputEvent对象,最后调用doProcessInputEvents方法:

《手把手教你读懂源码,View事件的注册和接收详细剖析》

只要当前待处理事件队列还有事件需要处理,就一直循环调用deliverInputEvent方法:

《手把手教你读懂源码,View事件的注册和接收详细剖析》

这里会根据q.shouldSendToSynthesizer()和q.shouldSkipIme()判断给stage赋值,然后调用stage的deliver方法。

《手把手教你读懂源码,View事件的注册和接收详细剖析》

看到这里,是否感觉到熟悉了,这就是setView方法最后设置各种不同类别输入事件分别调用的处理对象。在setView方法中一共生成了6个InputStage的子类对象,分别是ViewPostImeInputStage、NativePostImeInputStage、EarlyPostImeInputStage、ImeInputStage、ViewPreImeInputStage、NativePreImeInputStage。如果不是当前事件就不断指向下一个,直到找到对应的对象。

这里我们分析的是Touch事件,所以只关注ViewPostImeInputStage。

《手把手教你读懂源码,View事件的注册和接收详细剖析》

对于触摸事件,这里的(source InputDevice.SOURCE_CLASS_POINTER) != 0为true,所以会调用processPointerEvent方法。

《手把手教你读懂源码,View事件的注册和接收详细剖析》

这里的View就是窗口顶层视图DecroView,所以接下来继续分析View的dispatchPointerEvent方法:

《手把手教你读懂源码,View事件的注册和接收详细剖析》

这里调用了dispatchTouchEvent方法,由于DecorView重写了该方法,所以继续查看DecorView的dispatchTouchEvent方法:

《手把手教你读懂源码,View事件的注册和接收详细剖析》

这里做了一个判断,当cb!=null且mFeatureId0是执行cb.dispatchTouchEvent(ev),否则执行super.dispatchTouchEvent(ev),也就是经过FrameLayout继承ViewGroup的dispatchTouchEvent方法。

这里的cb对象是调用mWindow(即PhoneWindow对象)的getCallback方法获取的,是在之前Activity的attach方法中创建PhoneWindow对象后调用setCallback时被赋值的:

《手把手教你读懂源码,View事件的注册和接收详细剖析》

因此这里cb.dispatchTouchEvent(ev)即为Activity类的dispatchTouchEvent方法。

Touch事件传递到Activity后,就是我们经常看到的比较熟悉的Activity事件传递流程了。由于篇幅问题,我们下一篇再做具体分析,也欢迎关注,后续会继续推出更多精彩内容。

如果还有疑问的童鞋,欢迎留言继续讨论。

更多文章:

手把手教你读懂源码,View的加载流程详细剖析

手把手教你读懂源码,View的绘制流程详细剖析

经常被问到的有深度有内涵的数据结构面试题

巧用模板,不仅能提升AS开发效率,还能装逼

高薪安卓开发工程师必备技能——框架,看看你都掌握了哪些

Android常见内存泄露,学会这六招大大优化APP性能

这些移动应用常见性能测试指标,你都有重视吗?

史上最新最全的Android培训机构大揭秘

程序猿的工作和生活,你真的不懂

情人节里谁说程序员不懂浪漫

学会这几招,大幅提升效率效能

原来微信清粉,不仅显脑残,还是天大骗局,没想到那么多人上当

今天就先分享到这里,后续将推出更多精彩内容,欢迎一起探讨学习进步。

此文章版权为微信公众号分享达人秀(ShareExpert)——鑫鱻所有,若转载请备注出处,特此声明!

    原文作者:鑫鱻
    原文地址: https://www.jianshu.com/p/61561734ecd4
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞