在Android中,Touch事件的分发分服务端和应用端。
服务端
服务端由WindowManagerService(借助InputManagerService)负责采集和分发的,在应用端则是由ViewRootImpl(内部有一个mView变量指向View树的根,负责控制View树的UI绘制和事件消息的分发)负责分发的。
image.png当输入设备可用时,比如触屏,Linux内核会在/dev/input中创建对应的设备节点。
IMS(InputManagerService)监听/dev/input下的所有的设备节点,当设备节点有数据时会将数据进行加工处理并找到合适的Window(WMS寻找),将输入事件派发给他
- 事件信号是物理文件存储数据,位置:dev/input
- linux 有提供相关的文件监控api:inotify 、 epoll( ①inotify能监控文件变化产生FD ②epoll可以监控FD,以此配合完成文件的监控与监听)
- android 定义了两个线程来处理dev/input下面的信号
- 专门写了一个EventHub对象,里面用inotify+epoll对dev/input下进行监控!
- 将该对象放到InputReaderThread当中去执行,轮训getEvent(),这个里面有epoll_wait,相当于wait-notify机制,唤醒的触发点是/dev/input下的文件被改变,这个文件由驱动去推送数据
- InputReaderThread当中将/dev/input下的数据提取,封装,然后交给InputDispathcerThread
- InputDispathcerThread给最终选择到对应的ViewRootImpl(Window)
- 中间的通信机制通过socketpair进行,两边一人一组socketpair然后在ViewRootImpl中对于Channel的连接的文件进行监控,最终上层接受到touch信号。
client端
事件分发.PNG- 每一个Activity内部都包含一个Window用来管理要显示的视图。而Window是一个抽象类,其具体实现是 PhoneWindow类。
- DecovrView作为PhoneWindow的一个内部类,实际管理着具体视图的显示。他是FrameLayout的子类,盛放着我们的标题栏和根视图。我们自己写的一些列View和ViewGroup都是由他来管理的。
- 事件自顶向下的传递过程应该是这样的:
Activity(不处理)-> 根View -> 一层一层ViewGroup(如果有的话) -> 子View - 如果传递到最后我们的子View们没有处理这一事件怎么办呢?这时候就会原路返回,最终传递给Activity。只有当Activity也没有处理这一事件时,这一事件才会被丢弃。
Activity(不处理则丢弃) <- 根View <- 一层一层ViewGroup(如果有的话) <- 子View
具体在传递事件的时候,是由以下三个方法来控制的:
dispatchTouchEvent : 分发事件
onInterceptTouchEvent : 拦截事件
onTouchEvent : 消费事件
这三个方法有一个共同点,就是他们具体是否执行了自己的功能(分发、拦截、消费)完全由自己的返回值来确定,返回true就表示自己完成了自己的功能(分发、拦截、消费)。不同之处除了功能外,还有使用的场景。dispatchTouchEvent()和onTouchEvent()这两个方法,无论是Activity ViewGroup 还是View,都会被用到。而onInterceptTouchEvent()方法因为只是为了拦截事件,那么Activity和View一个在最顶层,一个在最底层,也就没必要使用了。因此在View 和 Activity中是没有onInterceptTouchEvent()方法的。
参考链接:https://blog.csdn.net/qq_42165012/article/details/123163462
网友评论