当屏幕被触摸,Linux
内核会将硬件产生的触摸事件包装为Event
存到/dev/input/event[x]
目录下
这样做的目的是将输入事件封装为通用的Event
,供后续处理
[](()1.2 SystemServer
部分
我们知道,当系统启动时,在SystemServer
进程会启动一系列系统服务,如AMS
,WMS
等
其中还有一个就是我们管理事件输入的InputManagerService
这个服务就是用来负责与硬件通信,接受屏幕输入事件。
在其内部,会启动一个读线程,也就是InputReader
,它会从系统也就是/dev/input/
目录拿到任务,并且分发给InputDispatcher
线程,然后进行统一的事件分发调度。
[](()1.3 跨进程通信传递给App
现在系统进程已经拿到输入事件了,但还需要传递给App
进程,这就涉及到跨进程通信的部分
我们的App
中的Window
与InputManagerService
之间的通信实际上使用的InputChannel
InputChannel
是一个pipe
,底层实际是通过socket
进行通信。
我们知道在Activity
启动时会调用ViewRootImpl.setView()
在ViewRootImpl.setView()
过程中,也会同时注册InputChannel
:
public final class ViewRootImpl {
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
requestLayout();
// …
// 创建InputChannel
mInputChannel = new InputChannel();
// 通过Binder在SystemServer进程中完成InputChannel的注册
mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
}
}
这里涉及到了WindowManagerService
和Binder
跨进程通信,读者不需要纠结于详细的细节
只需了解最终在SystemServer
进程中,WindowManagerService
根据当前的Window
创建了SocketPair
用于跨进程通信,同时并对App
进程中传过来的InputChannel
进行了注册
这之后,ViewRootImpl
里的InputChannel
就指向了正确的InputChannel
, 作为Client
端,其fd
与SystemServer
进程中Server
端的fd
组成SocketPair
, 它们就可以双向通信了。
然后我们App
进程的主线程就会监听socket
客户端,当收到消息(输入事件)后,回调NativeInputEventReceiver.handleEvent()
方法,最终会走到InputEventReceiver.dispachInputEvent
方法。
经过以上操作App
终于拿到输入事件了,接下来就是传递到对应页面
[](()1.4小结
关于内核处理输入事件与跨进程通信的部分一般来说不是应用开发者最关注的部分,也不是本文的重点,所以只做了概述
想要了解细节的同学可参考:[Input系统—事件处理全过程](()
[](()2.Touch
事件到达App
后怎么传递到对应页面
现在我们已经在App
进程中拿到输入事件了,接下来看看事件如何分发到页面
我们接下来跟一下源码
[](()2.1 事件回传到ViewRootImpl
//InputEventReceiver.java
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event);
}
//ViewRootImpl.java ::WindowInputEventReceiver
final class WindowInputEventReceiver extends InputEventReceiver {
public void onInputEvent(InputEvent event) {
enqueueInputEvent(event, this, 0, true);
}
}
//ViewRootImpl.java
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
adjustInputEventForCompatibility(event);
QueuedInp