Android系统源码阅读(13):Input消息的分发过程
请对照AOSP版本:6.0.1_r50。学校电脑好渣,看源码时卡半天
先回顾一下前两篇文章。在设备没有事件输入的时候,InputReader和InputDispatcher都处于睡眠状态。当输入事件发生,InputReader首先被激活,然后发送读取消息,激活Dispatcher。Dispatcher被激活以后,将消息发送给当前激活窗口的主线程,然后睡眠等待主线程处理完这个事件。主线程被激活后,会处理相应的消息,处理完毕后反馈给Dispatcher,从而Dispatcher可以继续发送消息。
1. InputReader获取事件
回顾一下第11章4.2中,InputReader线程在获取事件以后,会调用processEventsLocked(mEventBuffer, count);
处理事件。
1.1
这里先根据event的种类进行分门别类的处理。
frameworks/native/services/inputflinger/InputReader.cpp :
1.2
准备将事件交给设备进行处理。
frameworks/native/services/inputflinger/InputReader.cpp :
1.3
用device中的mapper去映射传入的事件,然后再处理。
frameworks/native/services/inputflinger/InputReader.cpp :
1.4
这里InputMapper种类很多,每个事件处理方法各不相同,所以在这里不再详述。其中有如下的InputMapper:
SwitchInputMapper, VibratorInputMapper, KeyboardInputMapper, CursorInputMapper, TouchInputMapper, SingleTouchInputMapper, MultiTouchInputMapper, JoystickInputMapper
这些方法处理到最后,会调用getListener()->notifyXXX(&args)
,让Dispatcher进行分发,XXX根据不同的Mapper有相应的名字。在创建InputReader时,将InputDispatcher作为参数传入,同时建立了QueuedInputListener来存放这个InputDispatcher,估计是准备将来处理多个InputDispatcher。所以这里getListener获取的就是当初建立的InputDispatcher对象。
1.5
这里虽然已经开始调用InputDispatcher的函数,但是还是在InputReader线程中。这里开始向InputDispatcher的队列中插入事件,并且把InputDispatcher唤醒了。因为notifyXXX函数同样是针对不同的输入有着不同的处理,所以不再详述,截取一段MotionEvent的代码片段。
frameworks/native/services/inputflinger/InputDispatcher.cpp :
将一个事件放入队列之后,会根据needWake参数决定是否要唤醒线程。如果要唤醒,则调用Looper的wake函数就可以了,和原来道理一样。在有些时候,有事件添加进去,不一定要唤醒线程,比如线程正在等待应用反馈事件处理完毕的消息。
1.6
实实在在的将这个EventEntry放入队列mInboundQueue中了。
frameworks/native/services/inputflinger/InputDispatcher.cpp :
这里做了两种优化,主要是在当前App窗口处理事件过慢,同时你又触发其他App的事件时,Dispatcher就会丢弃先前的事件,从这个开始唤醒Dispatcher。这样做很合情合理,用户在使用时,会遇到App由于开发者水平有限导致处理事件过慢情况,这时用户等的不耐烦,则应该让用户轻松的切换到其它App,而不是阻塞在那。所以,事件无法响应只会发生在App内部,而不会影响应用的切换,从而提升用户体验。App的质量问题不会影响系统的运转,Android在这点上做的很人性。
2. InputDispatcher分发事件
在第11章中3.2中,Dispatcher调用函数dispatchOnceInnerLocked(&nextWakeupTime);
来分配队列中的事件。
2.1
从队列中获取event,然后准备处理。
frameworks/native/services/inputflinger/InputDispatcher.cpp :
2.2
这一步根据Event的种类,略有不同。有dispatchMotionLocked
和dispatchKeyLocked
等。主要过程类似,首先判断是否需要丢弃该event,然后获得目标Window,再向目标window发送event。代码片如下:
frameworks/native/services/inputflinger/InputDispatcher.cpp :
这里调用findTouchedWindowTargetsLocked()
来获取目标Window。在前面12章3.6中,将mFocusedWindowHandle参数设置为了当前激活的Window,所以目前返回的inputTargets就是将mFocusedWindowHandle封装后的结果。
2.3
向每一个目标发送event。
frameworks/native/services/inputflinger/InputDispatcher.cpp :
2.4
frameworks/native/services/inputflinger/InputDispatcher.cpp :
2.5
这一步先判断目标Connection是否空,然后向其队列加入event。如果原来队列为空,说明可以进一步分发event;如果不为空,说明旧event还没有处理完毕,则不进一步分发。
frameworks/native/services/inputflinger/InputDispatcher.cpp :
2.6
循环的取出队列中的event,然后交给connection发送。
frameworks/native/services/inputflinger/InputDispatcher.cpp :
2.7
这一步同样有多种情况,有publishMotionEvent和publishKeyEvent。基本思路相近。先封装成message,最后都调用了mChannel->sendMessage(&msg)
。
frameworks/native/libs/input/InputTransport.cpp :
2.8
这一步就要像保存的文件描述符mFd
中写入数据了。
frameworks/native/libs/input/InputTransport.cpp :
到这里,Dispatcher终于把event通过当初建立的Channel pair发送给了应用window。这里和旧版本很不同,当初需要先将event放入共享内存,然后发送一个信号进行通知。
3. 当前激活Window获得消息
这一节比较复杂,需要回忆大量的前面几章的细节和一定的逻辑推理(连蒙带猜)能力。
先来回忆一下第12章4.5节,在InputChannel注册到Client时,最后一步做了什么。
frameworks/base/core/jni/android_view_InputEventReceiver.cpp :
这里给主线程的Looper添加的一个需要监听fd,这个fd是Client端的InputChannel的文件描述符。addFd
函数的第4个参数是一个回调函数,这里this指NativeInputEventReceiver对象,它是LooperCallback的子类。addFd
函数新建了一个Request对象,如下:
system/core/libutils/Looper.cpp :
再次回忆第10章1.6,也就是主线程被阻塞的地方。代码如下:
system/core/libutils/Looper.cpp :
这一步处理wake事件已经在第10章第1节讲述过了,这里需要处理的另一种事件input event。对input event,先将其放入mResponses数组,然后依次调用他们的回调函数。这里就是NativeInputEventReceiver的handleEvent函数了。
3.1
Event分为Input和Output,这里是事件输入,所以先看Input分支。
frameworks/base/core/jni/android_view_InputEventReceiver.cpp :
3.2
开始从目标Channel中读出event,然后包装成java层的对象,开始调用java函数进行处理。
frameworks/base/core/jni/android_view_InputEventReceiver.cpp :
我们先看如何从Channel中获取event的,3.3。然后讲解java层的分发过程,3.4。
3.3
从Chuannel中读取一个(一组)event。
frameworks/native/libs/input/InputTransport.cpp :
mChannel->receiveMessage(&mMsg)
函数在InputChannel中主要如下实现,调用socket函数recv从mFd中读出数据。
frameworks/native/libs/input/InputTransport.cpp :
3.4
回到java层,在获得event后,就需要进行分发了。receiverObj指向的是一个InputEventReceiver对象,这里其实是它的子类WindowInputEventReceiver对象,见第12章第4节。dispatchInputEvent
函数还是继承的父类的,没有重写。
3.5
这一步直接调用了下一步。
3.6
将event插入等待事件队列的尾部,然后开始调度这些消息。
frameworks/base/core/java/android/view/ViewRootImpl.java :
这里会有两种不同的处理event的方式,一个是立即处理;另一种是向主线程Looper发送MSG_PROCESS_INPUT_EVENTS
消息。这里选择立即处理。
3.7
这里会把等待在队列中的event,一口气全处理了。这一步会循环拿出队列中的每一个event,然后调用下一步进行处理。
3.8
准备交给stage处理。
frameworks/base/core/java/android/view/ViewRootImpl.java :
3.9
下面就讲解一下stage是什么东西。stage可以说是处理event的不同阶段,如果上一个stage处理不了,就交给下一个stage处理,总有一个stage可以将event处理掉。这里stage的处理顺序图如下所示:
- NativePrelmeInputStage: Delivers pre-ime input events to a native activity. Does not support pointer events. 其实我也不清楚到底是干什么的,没有想到具体的应用场景。
- ViewPreImeInputStage: Delivers pre-ime input events to the view hierarchy. Does not support pointer events. 这一步只处理KeyEvent,在InputMethod处理这个KeyEvent之前,可以截获这个event。典型的例子是处理BACK key。
- ImeInputStage: Delivers input events to the ime. Does not support pointer events. 将event交给IputMethodManager处理。
- EarlyPostImeInputStage: Performs early processing of post-ime input events. 在交给下一阶段之前,先处理筛选一些event。
- NativePostImeInputStage: Delivers post-ime input events to a native activity. 尝试让InputQueue发送event给native activity。
- ViewPostImeInputStage: Delivers post-ime input events to the view hierarchy. 将event发送给view的层次结构中。
- SyntheticInputStage: Performs synthesis of new input events from unhandled input events. 最后一个阶段,处理综合事件,比如trackball, joystick等。
这里我们暂且研究第6种stage,这个stage和View有直接关系。
3.10
这里根据event的类型分别进行处理。我们先关注一下PointerEvent如何处理的。
3.11
将event交给mView来处理,这里mView就是一个DecorView对象。
3.12
这一步是DecorView继承自View的方法,将event分为TouchEvent和GenericMotionEvent来处理。先看TouchEvent如何处理。
3.13
DecorView重写了该函数。这里调用getCallback
函数来获取一个回调对象。该函数是PhoneWindow继承自Window类的方法,获得是mCallback。那么这个mCallback到底是谁呢?
回顾一下Activity的创建过程,在Activity创建以后会调用attach函数对Activity进行一定的初始化,其中就创建了PhoneWindow,同时设置了callback为Activity它自己。所以这一步获得是一个Activity对象,然后调用它的函数继续分发event。
3.14
Event交到Activity手中进行处理。为什么会先交给Activity处理?目的是让开发者可以重写这个函数,从而可以在分发这个事件之前进行截获。
|
|
3.15
什么也没做,将event传回DecorView,让它处理。
###3.16
这里DecorView又交给dispatchTouchEvent处理。这里的dispatchTouchEvent是源自父类ViewGroup的函数,而不是自己重写的函数。
###3.17
ViewGroup是View的子类。它管理了一组View在mChildren数组中,按照设计模式的说法叫Composite模式。
这一步会依次访问每个子view,判断他们是否可以处理该event,如果能就交给它处理;没人能处理就自己处理。无论哪种方式,都会调用下一步dispatchTransformedTouchEvent
函数。
frameworks/base/core/java/android/view/ViewGroup.java :
3.18
ViewGroup会决定自己处理还是交给child处理。在ViewGroup自己处理,或者child为不再是一个ViewGroup时,则开始调用View的dispatchTouchEvent函数。
frameworks/base/core/java/android/view/ViewGroup.java :
3.19
这一步就会调用用户自己实现的Listener;如果没有Listener,则会调用view默认的处理函数。
frameworks/base/core/java/android/view/View.java :
默认处理函数onTouchEvent会处理一些基本的操作,比如button的按下松开的效果,滚动容器产生滚动的效果等。
3.20
在InputStage完成处理event的任务后,会开始返回完成的消息。
3.21
开始调用c++函数。
3.22
将指针转化为NativeInputEventReceiver指针,交给它来处理。
frameworks/base/core/jni/android_view_InputEventReceiver.cpp :
3.23
InputConsumer依次处理每个sequence,发送完成信号。
3.24
向channel发送完成消息。
|
|
向Channel写入事件后,处于睡眠的InputDispatcher被唤醒,开始分发下一个event。
最后说两句
到这里,Input event的处理流程已经分析完了,心好累。