基于Android R版本分析
Input
参考:Android IMS原理解析之InputChannel;
Input Service
Input系统和应用层之间属于跨进程通信,两者通过FD的方式关联,可以简单的理解为:InputDispatcher代表了系统侧,InputEventReceiver代表了应用进程侧;
InputDispatcher属于SystemServer进程,大致执行两件事件:
- 监听InputReader侧发送过来的驱动侧的event数据;
- 将Touch Event事件传递给应用层进行消费并接收应用层的响应;
InputEventReceiver属于App 进程,同样也是执行了两件事件:
- dispatchInputEvent:将Touch Event数据分发到指定的Activity或者是View中;
- finishInputEvent:通知InputDispatcher表明Touch Event事件已经被消费;
在ANR的概念中,我们知道在触摸或者点击屏幕超时时会出现ANR异常,其原理就是通过上述的逻辑实现的;
InputChannel
InputChannel本质就是local unix domain socket,创建时,用到了socketpair()函数;
SocketPair(非网络套接字)用来实现在本机内进行进程间的通信。一对SocketPair通过socketpair()函数创建,其使用者可以因此而得到两个相互连接的文件描述符。这两个描述符可以通过套接字接口send()和recv()进行写入和读取,并且向其中一个文件描述符写入的数据,可以从另一个描述符中读取。同pipe()所创建的管道不同,SocketPair的两个文件描述符是双通的,因此非常适合用来进行进程间的交互式通信;
InputChannel 创建
我们知道,调用setContentView()或者是WindowManager.addView()方法,最终都会调用到WindowManagerGlobal类中的addView()方法,在该方法中创建了ViewRootImpl实例,然后会调用ViewRootImpl的setView()方法,这个是InputChannel创建的起点,在setView()方法中会通过IWindowSession跨进程调用到WMS中;
InputChannel的创建是在ViewRootImpl中创建的,即是在应用进程侧创建的,然后通过IWindowSession跨进程的方式传入到WMS中;
InputChannel传入到WMS中,会绑定对应的WindowState,调用WindowState的openInputChannel()方法开启client端传入的InputChannel实例;
在WindowState中,会调用native层的openInputChannel()函数,为该WindowState创建一个InputChannel数组,其中包含两个InputChannel:
- mInputChannel = inputChannels[0]:代表了Server端的NativeInputChannel
- mClientChannel = inputChannels[1]:代表了Client端的NativeInputChannel
两个NativeInputChannel创建成功之后,需要分别对这两种InputChannel进行绑定操作;
Server InputChannel
这个过程中,执行了两件事:
- 为其创建对应的Connnection实例,这个是非常重要的概念,后续的事件分发,都是依赖于Connection;
- 调用addFd()函数,监听Client侧发送过来的Event事件,一般指的就是Finish Input Event事件;
Client InputChannel
Client InputChannel过程中的transferTo()方法中,其实就是将Java层面的Client InputChannel和Native层面的Client InputChannel进行关联,以满足后续可以通过Java层面的InputChannel和Native层面的InputChannel进行通信;
InputEventReceiver
InputEventReceiver对象可以接收来自InputChannel的输入事件,并触发其onInputEvent()回调;
InputEventReceiver的类型为abstract类型,我们一般在Activity中使用到的InputEventReceiver为WindowInputEventReceiver,WindowInputEventReceiver定义在ViewRootImpl中,在setView()时创建了该实例;
init InputEventReceiver
在创建WindowInputEventReceiver实例的时候,调用了native层的init()函数,用于初始化InputEventReceiver,在native层中获取到了对应的Client NativeInputChannel对应的clientFd,并将其Fd注册到了InputDispatcher的Looper中进行监听fd的变化;
dispatch Event
当我们触摸屏幕的时候,设备驱动将触摸数据发送给Event HUB,Event HUB通过InputReader将数据最终发送给InputDispatcher,在InputDispatcher中,需要根据触摸区域找到对应响应的InputWindowHandle,然后通过InputWindowInfo来向对应的Window窗口dispatch Touch Event;