前序
一般情况下很多同学对于点击事件的认识都只存在于从 Activity 开始的,然后从 Window 中进行分发,或者有些人有过自定义View的经验可能会涉及事件拦截处理等,会用到 onTouchEvent
和 dispatchTouchEvetn
这几个方法,会处理屏幕滑动,Down事件,Up事件等,但仅停留于对于 View 层的了解。
自定义View的事件处理其实在整个Android输入系统中只能算是最上层的,但这些事件是怎么产生的,上层View是怎么获得到这些事件的呢?喜欢研究底层实现原理的我,今天就给大家讲讲。
输入系统
事件产生的底层主要是输入子系统,Android 中的输入设备有很多,例如屏幕,鼠标,键盘等都是输入设备,对于应用开发者,接触最多的也就是屏幕了。当输入设备可用时,Linux会在 /dev/input 中创建对应的设备节点。用户操作输入设备就会产生各种事件,这些事件的原始信息就会被 Linux内核中的输入子系统采集,原始信息由 Kernel space 的驱动层一直传递到设备结点。
Android提供了一些api可以让开发者在设备节点(dev/input/)中读取内核写入的事件。
InputManagerService
IMS 的工作就是监听 /dev/input 下所有设备的节点,当有数据时就会进行加工处理,并交给中间层的 WMS 派发给合适的 Window。后面简称为IMS。
WindowManagerService
Android的WMS主要是负责窗口管理,窗口的启动、添加、删除,管理窗口大小、层级等,WMS与IMS息息相关。IMS将底层封装好的事件通过Socket pair传递给WMS,WMS通过绑定好的InputChannel找到对应的窗口进行app层的事件分发,这时候就到我们熟悉的Activity的事件分发流程了。
下面借张图,整体流程画的非常详细:
今天我们主要讲底层硬件驱动产生的事件的获取及分发流程,至于硬件驱动层就不展开介绍了。而这里面最重要的就是IMS相关的流程,下面开始补足知识盲区。
IMS的初始化
IMS的构造
和WMS一样,IMS 也是在 SystemServer 中创建的 ,也是 startOtherServers 方法中。
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
inputManager = new InputManagerService(context);
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
...
inputManager.start();
}
主要做了三件事:
-
调用了IMS的构造方法
-
将IMS作为参数传递给WMS
-
调用了IMS的启动方法start
IMS 构造函数
public InputManagerService(Context context) {
this.mContext = context;
//注释1
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
mStaticAssociations = loadStaticInputPortAssociations();
mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
+ mUseDevInputEventForAudioJack);
//注释2
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
}
我们重点关注1创建了一个在android.display 线程的InputManagerHandler,所以后面这个handler处理的消息都是执行在android.display 线程中,android.display 线程是系统共享的单例前台线程,可以用做一些低延时显示的相关操作,WMS 的创建也是在 android.display 中创建的。
注释2 的话,调用了nativeInit 方法,进入native侧创建了 NativeInputManager ,并将该对象指针返回给了 java层,方便后续调用处理。
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == nullptr) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast<jlong>(im);
}
接下来看下NativeInputManager的创建