请对照AOSP版本:6.0.1_r50。
InputManager可以获得输入事件并分发,Activity需要处理这些输入事件。那么,这两者之间如何建立的连接呢?这就需要InputChannel作为桥梁建立两者之间的通道。
1. ViewRootImpl创建InputChannel
这里ViewRoot类已经消失了,由ViewRootImpl替代。Activity在创建时会将自己的DecorView设置给对应的ViewRootImpl。
1.1
这一步会创建client的InputChannel,并且将当前启动的Activity的窗口传递给WindowManagerService。
frameworks/base/core/java/android/view/ViewRootImpl.java
1.2
这一步是进程间的请求,从应用进程转到WindowManagerService进程,对应于1.1中的函数addToDisplay。这里会补足一些参数,开始调用下一步函数。
1.3
这一步也是调整一些参数,然后交给WindowManagerService来处理。
1.4
这里会将传入的Window存入Map来进行统一管理,同时创建了一对server/client端的InputChannel。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java :
1.5
这一步会将任务交给c++层来处理。
1.6
这一步对应的c++的函数为android_view_InputChannel_nativeOpenInputChannelPair
,它会创建两个InputChannel,并返回。
frameworks/base/core/jni/android_view_InputChannel.cpp :
1.7
注意,这一步真的要创建ChannelPair了。
frameworks/native/libs/input/InputTransport.cpp :
socketpair创建了一对无名的套接字描述符(只能在AF_UNIX域中使用),描述符存储于一个二元数组s[2] .这对套接字可以进行双工通信,每一个描述符既可以读也可以写。这个在同一个进程中也可以进行通信,向s[0]中写入,就可以从s[1]中读取(只能从s[1]中读取),也可以在s[1]中写入,然后从s[0]中读取;但是,若没有在0端写入,而从1端读取,则1端的读取操作会阻塞,即使在1端写入,也不能从1读取,仍然阻塞;反之亦然。该段解释来自。
这里new了两个InputChannel,该类的构造函数如下:
frameworks/native/libs/input/InputTransport.cpp :
这里将描述符mFd设置为nonblock。
显然,这里和Android 2.3版本有着很大区别。在旧版本中使用的是匿名共享内存和两个pipe来实现双向的通信。显然,新版本利用的linux系统的新机制,更为简洁高效。
以上步骤实在新Activity建立,窗口开始创建时执行的。这里主要就让WindowManagerService建立一对InputChannel,将Activity的Window和InputDispatcher建立起连接,从而传输输入事件。
2. Server端注册InputChannel
在1.4中,创建了一对InputChannel,其中Server端的InputChannel会注册进InputManagerService。
|
|
2.1
这一步就检验了一下传入的InputChannel是否为空。下面一言不合就开始调用native函数。
2.2
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp :
2.3
这一步通过NativeInputManager中的InputManager,InputManager通过InputDispatcher来注册InputChannel。
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp :
2.4
终于,将Server端的InputChannel交给了InputDispatcher。
frameworks/native/services/inputflinger/InputDispatcher.cpp :
到这一步,InputDispatcher已经将InputChannel的Server端管理起来了。当Dispatcher将一个事件通过Channel发送给应用程序窗口以后,会进入休眠状态,直到应用窗口再次通过这个Channel返回一个消息将其激活,Dispatcher才准备发送下一个消息。
这里创建的Connection,Connection中又创建了InputPublisher。InputPublisher可以直接将消息通过这个InputChannel发送出去。
3. 向InputManagerService注册当前激活的应用程序窗口
再次回顾一下1.4的,在1.4中WindowManagerService在焦点发生改变时,需要改变Focused Window。这里会在InputMonitor中注册当前激活的窗口。
3.1
在InputMonitor中,有mInputFocus保存着当前激活的窗口,这里会将mInputFocus设置为传入的newWindow。然后调用updateInputWindowLW继续更新激活的窗口。
3.2
在1.4中,每个window会被加入一个Window List。这里会遍历这些List中的Windows,然后将这些windows进一步交给InputManagerService处理。
rameworks/base/services/core/java/com/android/server/wm/InputMonitor.java :
3.3
这一步InputManagerService将任务交给了c++层。
3.4
进入c++层,将这些windowHandles交给NativeInputManager。
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp :
3.5
这一步先将java的windowHandle变为c++对象,然后这些windowHandle被交给InputDispatcher处理。
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp :
3.6
InputDispatcher跟新WindowHandle,并且更新Focused window。
frameworks/native/services/inputflinger/InputDispatcher.cpp :
到这里,InputDispatcher获取了和激活的Window的双向通信通道,同时Dispatcher也知道了是哪个Window处于Focused状态。只要Client端将通信通道建立完毕,则Dispatcher可以向Activity的Window发送消息了。
4. Client端注册InputChannel
|
|
在1.1中,通过WindowSession添加Window和InputChannel后,mInputChannel对象已经转化为双向通道中的client端通道了,从1.4可以知道。然后,该步骤又创建了一个WindowInputEventReceiver类的对象mInputEventReceiver,它将mInputChannel和主线程绑定在一起。
下面就看一下WindowInputEventReceiver的构造过程。
4.1
WindowInputEventReceiver是InputEventReceiver的子类,具体构造过程在InputEventReceiver中。
4.2
这一步又开始调用native函数。
frameworks/base/core/java/android/view/InputEventReceiver.java:
4.3
创建c++层的NativeInputEventReceiver。
frameworks/base/core/jni/android_view_InputEventReceiver.cpp :
NativeInputEventReceiver的构造函数。
frameworks/base/core/jni/android_view_InputEventReceiver.cpp :
4.4
这一步调用函数setFdEvents,参数为ALOOPER_EVENT_INPUT
。
4.5
将传入的Channel让主线程监听起来,以便处理传入的消息。
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
这里监听的文件描述符发生IO事件时,调用的回调函数就是NativeInputEventReceiver自己,因为它为LooperCallback子类。
同时注意这里的Looper就是应用主线程的Looper,这里向其epoll多添加了一个文件描述符进行监听,因为epoll可以同时监听多个epoll的IO事件。同时设定了监听的类型为ALOOPER_EVENT_INPUT。
回忆下第10章的1.6步骤中epoll_wait进入的睡眠后,会监听mWakeEventFd文件描述符的事件。不仅如此,这一步还会监听更多的文件描述符的事件,这里就包括建立的InputChannel的文件描述符。所以在主线程通过调用pollInner进入睡眠以后,被唤醒后会判断发生事件的文件描述符是哪一个。
system/core/libutils/Looper.cpp :
到这里,InputChannel在Client端也进行了监听,整个完整的window所在的应用主线程和InputDispatcher线程之间的双向Channel已经建立完毕,同时InputDispatcher知道哪一个window处于激活状态,因为它知道向哪一个window发送消息。