Android 事件处理系统

https://www.cnblogs.com/lcw/p/3373214.html

1. Android 事件传递

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

为什么使用共享内存而不是直接使用Binder机制?

Google 为了保证事件响应的实时性,在选择进程间传递事件的方式中,选择了共享内存的方式。
由于共享内存在数据管理过程中基本不涉及到内存的数据拷贝,只是在进程读写时涉及到2次数据拷贝,这个是不可避免的数据拷贝,因此这种方式能够很好的保证系统对事件的响应,但是仅仅是共享内存是不够的,因为共享内存的通信方式并不能够通知对方有数据更新,因此android 在事件处理过程中加入了另一种进程间通信方式 管道(pipe)。

管道的效率不如共享内存高,会不会影响事件处理的实时性?

没关系,每次system_serve 通知ViewRoot 只是向其传递一个字符,既轻巧又简单。

事件处理系统的初始化过程

Android 事件传递系统是以共享内存和管道的进程间通信方式来实现传递的。

创建管道连接

 管道的主要作用是在有事件被存储到共享内存中时,system_server 端通知ViewRoot去读取事件的通信机制。

ViewRoot 和 WindowManagerService (负责事件传递,运行在system_server进程中) 各需维护管道的一个文件描述符。实现全双工的管道通信。
WindowManagerService —> ViewRoot 方向的管道通信,表示WMS通知ViewRoot 有新事件被写入到共享内存。
ViewRoot --> WindowManagerService 方向的管道通信,表示ViewRoot 已经消化完共享内存中的新事件,特此通知WMS。
ViewRoot 和 WindowManagerService 的管道的文件描述符都是被存储在一个名为InputChannel 的类中,这个InputChannel 类是管道通信的载体。

在创建InputChannel 对的同时,会申请共享内存,并向2个InputChannel 对象中各自保存一个共享内存的文件描述符。

一个管道通信只是对应一个Activity 的事件处理,也就是当前系统中有多少个Activity 就会有多少个全双工管道。
此时系统需要一个管理者来管理以及调度每一个管道通信
因此在创建完InputChannel 对象后,需要将其注册到这个管理者中去。

ViewRoot 端的一般情况下会注册到一个NativeInputQueue 对象中
WMS 端注册在InputManager对象中。

注册到NativeInputQueue
ViewRoot 端InputChannel 对象在向NativeInputQueue注册时,需要注册3个参数:

将InputChannel 对象对应的Native InputChannel 传递给 NativeInputQueue;
将ViewRoot 的成员变量InputHandler 传递给NativeInputQueue,这个InputHandler则是事件的处理函数,传递它的主要作用是明确当前ViewRoot的事件处理函数。
当前Application 的主进程的 MessageQueue

Android 在实现事件传输时,很大程度上借用了线程Looper 和 MessageQueue 的轮询(poll ) 机制,通过它的轮询机制来检测管道上是否有消息通知事件发生,借用Looper机制能够很大限度的保证事件能够第一时间被Application知晓。

注册过程中,android 会将InputChannel 对象中保存的管道的文件描述符交给MessageQueue 的 native looper 去监听,同时向native looper 指示一个回调函数,一旦有事件发生,native looper 就会检测到管道上的数据,同时会去调用指示的回调函数。
handleReceviceCallback()@android_view_InputQueue.cpp

NativeInputQueue 对象,真个系统只有一个,它为了负责管理这么多的Application的事件传递
android 在NativeInputQueue 类中定义了一个子类Connection,每个InputChannel 对象在注册时都会创建一个自己的Connection对象。
registerInputChannel()@android_view_InputQueue.cpp

注册到InputManager

不采用Looper 来轮询是否有事件发生,InputManager 启动了2个进程来管理事件发生与传递,
InputReaderThread 和 InputDispatcherThead, InputReaderThread进程负责轮询事件发生;
InputDispatcherThread 负责dispatch 事件。(如果用一个进程来管理,在轮询input 系统event的事件间隔会变长,有可能丢失事件)。

虽然没有使用Looper 来轮询事件的发生,但是InputDispatcher 使用了native looper(并不是WMS线程的,而是线程InputDispatcher自定义的) 来轮询检查管道通信,这个管道通信表示InputQueue是否消化完成dispatch 过去的事件。

WMS 在初始化时会创建这么一个InputManager 实例,是系统唯一的。
JAVA 层的InputManager 实例并没有实现太多的业务,真正实现Input Manager 业务是Native的
NatuveInputManager实例,它在被创建时,建立起了整个WMS端事件传递系统的静态逻辑。

InputDispatcher
数据传输管理的核心业务是在InputDispatcher中完成的,因此最终WMS端InputChannel 对象会注册到InputDispatcher 中,同样的由于整个系统中InputDispatcher 实例只有一个,而WMS 端InputChannel对象是和ViewRoot 一一对应的。
InputDispatcher 中的Connect类的核心业务是由InputPublisher 对象来实现的,该对象负责将发生的事件信息写入到共享内存。

Notify InputDispatcher表示不同的事件通知InputDispatcher的函数调用,这几个函数虽然是被InputReaderThread调用的,单却是在InputDispatcher定义的。

InputDispatcherThread线程操作
InputDispatcherThread线程的轮询过程dispatchOnce()–>dispatchOnceInnerLocked()。
InputDispatcherThread线程不停的执行该操作,以达到轮询的目的,重点也就是这2个函数处理了。
InputDispatcherThread基本流程
InputDispatcherThread的主要操作是分两块同时进行的,
一部分是对InputReader传递过来的事件进行dispatch前处理,比如确定focus window,特殊按键处理如HOME/ENDCALL等,在预处理完成 后,InputDispatcher会将事件存储到对应的focus window的outBoundQueue,这个outBoundQueue队列是InputDispatcher::Connection的成员函数,因此它是和ViewRoot相关的。
一部分是对looper的轮询,这个轮询过程是检查NativeInputQueue是否处理完成上一个事件,如果NativeInputQueue处理完成事件,它就会向通过管道向InputDispatcher发送消息指示consume完成,只有NativeInputQueue consume完成一个事件,InputDispatcher才会向共享内存写入另一个事件。

事件传递

InputReaderThread 线程操作
当input系统有事件发生时,会被InputReaderThread 线程轮询到
InputReader 会根据事件的device id 来选择的InputDevice
然后再根据事件的类型来选择InputDevice中的InputMapper,InputMapper会将事件信息通知给InputDispatcher

总结输入事件的处理流程

首先从Kernel 传递上来的键值由EventHub进行转码,之后由InputReader将其解释成各个事件,再由InputDispatcher分发。
InputManager是InputReader和InputDispatcher 线程的创建者,它只有一个职责,就是被WindowManagerServic e使用,从Native层获取按键事件。

WindowManagerService 则负责与窗口对接,分发按键消息。

源码位置

事件分发给最前面的窗口:
/frameworks/base/services/java/com/android/server/WindowManagerService.java
拦截消息的处理类:
/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
按键事件定义:
/frameworks/base/core/java/android/view/KeyEvent.java
Java层输入管理:
/frameworks/base/services/java/com/android/server/InputManager.Java
native层输入管理:
/frameworks/base/libs/ui/InputManager.cpp
事件读取线程:
/frameworks/base/libs/ui/InputReader.cpp
事件分发线程:
/frameworks/base/libs/ui/InputDispatcher.cpp
键码与键值转换:
/frameworks/base/libs/ui/EventHub.cpp

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值