Input子系统是一个庞大的系统,为了简单明了的介绍该系统,我们采用分模块以及倒序追踪的方式来一步步解开它的面纱。
一、java层事件传递过程
我们从这个button被点击来研究input子系统中事件的传递。
废话不多说,对button加一个OnTouchListener,在其onTouch方法上加一个断点,直接利用eclipse的debug工具查看touch事件的传递过程(倒序)
MainActivity$1.onTouch(View, MotionEvent) line: 24
Button(View).dispatchTouchEvent(MotionEvent) line: 8582
RelativeLayout(ViewGroup).dispatchTransformedTouchEvent(MotionEvent, boolean, View, int) line: 2519
RelativeLayout(ViewGroup).dispatchTouchEvent(MotionEvent) line: 2110
FrameLayout(ViewGroup).dispatchTransformedTouchEvent(MotionEvent, boolean, View, int) line: 2519
FrameLayout(ViewGroup).dispatchTouchEvent(MotionEvent) line: 2110
ActionBarOverlayLayout(ViewGroup).dispatchTransformedTouchEvent(MotionEvent, boolean, View, int) line: 2519
ActionBarOverlayLayout(ViewGroup).dispatchTouchEvent(MotionEvent) line: 2110
PhoneWindow$DecorView(ViewGroup).dispatchTransformedTouchEvent(MotionEvent, boolean, View, int) line: 2519
PhoneWindow$DecorView(ViewGroup).dispatchTouchEvent(MotionEvent) line: 2110
PhoneWindow$DecorView.superDispatchTouchEvent(MotionEvent) line: 2482
PhoneWindow.superDispatchTouchEvent(MotionEvent) line: 1798
MainActivity(Activity).dispatchTouchEvent(MotionEvent) line: 2797
PhoneWindow$DecorView.dispatchTouchEvent(MotionEvent) line: 2443
PhoneWindow$DecorView(View).dispatchPointerEvent(MotionEvent) line: 8799
ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl$QueuedInputEvent) line: 4663
ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl$QueuedInputEvent) line: 4521
ViewRootImpl$ViewPostImeInputStage(ViewRootImpl$InputStage).deliver(ViewRootImpl$QueuedInputEvent) line: 4035
ViewRootImpl$NativePostImeInputStage(ViewRootImpl$InputStage).onDeliverToNext(ViewRootImpl$QueuedInputEvent) line: 4088
ViewRootImpl$NativePostImeInputStage(ViewRootImpl$InputStage).forward(ViewRootImpl$QueuedInputEvent) line: 4054
ViewRootImpl$NativePostImeInputStage(ViewRootImpl$AsyncInputStage).forward(ViewRootImpl$QueuedInputEvent) line: 4191
ViewRootImpl$NativePostImeInputStage(ViewRootImpl$InputStage).apply(ViewRootImpl$QueuedInputEvent, int) line: 4062
ViewRootImpl$NativePostImeInputStage(ViewRootImpl$AsyncInputStage).apply(ViewRootImpl$QueuedInputEvent, int) line: 4248
ViewRootImpl$NativePostImeInputStage(ViewRootImpl$InputStage).deliver(ViewRootImpl$QueuedInputEvent) line: 4035
ViewRootImpl$EarlyPostImeInputStage(ViewRootImpl$InputStage).onDeliverToNext(ViewRootImpl$QueuedInputEvent) line: 4088
ViewRootImpl$EarlyPostImeInputStage(ViewRootImpl$InputStage).forward(ViewRootImpl$QueuedInputEvent) line: 4054
ViewRootImpl$EarlyPostImeInputStage(ViewRootImpl$InputStage).apply(ViewRootImpl$QueuedInputEvent, int) line: 4062
ViewRootImpl$EarlyPostImeInputStage(ViewRootImpl$InputStage).deliver(ViewRootImpl$QueuedInputEvent) line: 4035
ViewRootImpl.deliverInputEvent(ViewRootImpl$QueuedInputEvent) line: 6464
ViewRootImpl.doProcessInputEvents() line: 6438
ViewRootImpl.enqueueInputEvent(InputEvent, InputEventReceiver, int, boolean) line: 6391
ViewRootImpl$WindowInputEventReceiver.onInputEvent(InputEvent) line: 6623
ViewRootImpl$WindowInputEventReceiver(InputEventReceiver).dispatchInputEvent(int, InputEvent) line: 185
MessageQueue.nativePollOnce(long, int) line: not available [native method]
MessageQueue.next() line: 148
Looper.loop() line: 151
ActivityThread.main(String[]) line: 5637
Method.invoke(Object, Object[], boolean) line: not available [native method]
Method.invoke(Object, Object...) line: 372
ZygoteInit$MethodAndArgsCaller.run() line: 959
ZygoteInit.main(String[]) line: 754
下面对这个关系链按着正序进行分析:
首先看一下整个的调用流程图
几点说明:
1.InputEventReceiver的onInputEvent方法被回调,开启了java层touch事件传递,至于InputEventReceiver是被谁回调的,这里不得而知,我们稍后再分析。
2.InputStage. NativePreImeInputStage、ViewPreImeInputStage、ImeInputStage、EarlyPostImeInputStage、NativePostImeInputStage、ViewPostImeInputStage、SyntheticInputStage构成一个输入事件责任处理链,如果本阶段对事件没有处理,则传递到下一个对象进行处理,直至事件被处理。NativePreImeInputStage、ViewPreImeInputStage、ImeInputStage三个类用来实现输入法的按键派发和处理,如果事件不传递到输入法服务中,这三个类可以跳过,直接从EarlyPostImeInputStage对象开始处理,在ViewPostImeInputStage对象处理阶段调用了主View 对象(对应PhoneWindow中的DecorView对象)的事件提交函数如(dispatchTouchEvent)函数向视图对象提交输入事件,在当前窗口的视图树中派发事件。
3.MainActivity. DecorView在传递事件过程中首先将事件传给Activity的dispatchTouchEvent()方法,所以我们可以重写这个方法来阻止事件继续传递,在手表项目中为了屏蔽应用左滑退出的功能,就采用这种方式做到的。
4.ViewGroup. 这是touch事件在层层viewgroup中传递的过程,如果某个viewgroup拦截了事件,譬如手表中交互是左滑退出,这里面实际上就是SwipeDismissLayout检测到左滑手势时回调OnDismissedListener的onDismissed()方法,最终finish当前activity.
下面是app布局的层级关系hierarchy view图
这个图里面我们可以看出,decorView是根view,但SwipeDismisslayout是怎么进到这里面来的呢?在5.0之后,只要加上
那到底framework是在哪里识别这个标签的呢?
这是一个app创建布局的过程,不细说了。其中generatelayout这个方法就是在加载我们写的布局文件之前根据feature添加一些中间的viewgroup,譬如SwipeDismisslayout.
再比如下面是一个带actionbar的应用的hierarchy view,这是多么的复杂啊,实际上我们添加的只是最后的Button,而framework给我们追加了一个复杂的ActionBarOverlayLayout.
5.touch事件在ViewGroup中传递细节:
这里面需要注意几点:
i.action_down事件,如果onInterceptTouchEvent返回true,viewgroup就会拦截touch事件,由自己的onTouchEvent方法处理,并且之后的事件不经过onInterceptTouchEvent,直接由onTouchEvent处理
ii. action_down事件,如果子view的onTouchEvent返回false,那么之后的事件就不会再给这个子view,由viewgroup自己的onTouchEvent处理
产生上面两点的原因是mFirstTouchTarget==null,mFirstTouchTarget可以看成是每次从action_down开始接收事件的target,即现在viewGroup没有找到事件接受者,只能自己处理,intercepted = true
dispatchTouchEvent(MotionEvent ev) {
..............
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
...............
}
protected boolean isTransformedTouchPointInView(float x, float y, View child,
PointF outLocalPoint) {
final float[] point = getTempPoint();
point[0] = x;
point[1] = y;
transformPointToViewLocal(point, child);
final boolean isInView = child.pointInView(point[0], point[1]);
if (isInView && outLocalPoint != null) {
outLocalPoint.set(point[0], point[1]);
}
return isInView;
}
/**
* Determines whether the given point, in local coordinates is inside the view.
*/
/*package*/ final boolean pointInView(float localX, float localY) {
return localX >= 0 && localX < (mRight - mLeft)
&& localY >= 0 && localY < (mBottom - mTop);
}
6.多点触控:
这里主要说一下MotionEvent的Action是怎么存储多点信息的
mAction的低8位(也就是0-7位)是动作类型信息。
mAction的8-15位呢,是触控点的索引信息。(即表示是哪一个触控点的事件)。
这是一个高效的存储方式,不但记录的事件类型,还说明了是哪个触摸点的事件。
所以我们可以看到MotionEvent有两个方法,一个是getAction(),另一个是getActionMasked(),实际上第二个方法就是通过掩码的方式隐去触控点的索引信息,只记录事件类型
/**
* Bit mask of the parts of the action code that are the action itself.
*/
public static final int ACTION_MASK = 0xff;
public final int getActionMasked() {
return mAction & ACTION_MASK;
}
我们再来看一下如何得索引值呢?这也是一个利用位运算的高效写法,可以学习一下
Public final int getActionIndex() {
return (mAction & 0xff00) >> 8;
}
好了,以上就是事件在java层的传递过程,下面我们分析c++层
二.C++层事件处理
在之前java层的事件传递流程中,我们看到最开始的时候是InputEventReceiver的onInputEvent方法被回调,那这个方法是被谁调用的呢?带着这个疑问我们进入C++层探寻。
这是InputEventReceiver初始化流程,包括java和c层,我们看到最后Looper监听一个文件描述符,(代码位于frameworks/base/core/jni/android_view_InputEventReceiver.cpp):
int fd = mInputConsumer.getChannel()->getFd();
mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
这里是epoll机制(关于Looper中的epool原理,可参考Looper中的epool机制),至于这个监听的fd是何方神圣现在还不知道,但是addFd的第四个参数就是监听到事件之后的回调,果然在NativeInputEventReceiver中实现了handleEvent方法,查看该方法实现:
1.NativeInputEventReceiver::handleEvent(171) status_t status = consumeEvents(env, false , -1, NULL);
2.NativeInputEventReceiver::consumeEvents(313)
env->CallVoidMethod(receiverObj.get(),gInputEventReceiverClassInfo.dispatchInputEvent,seq, inputEventObj);
这里就是native层调用java层代码,即调用InputEventReceiver.java的dispatchInputEvent方法
private void dispatchInputEvent(int seq, InputEvent event) {
.......
onInputEvent(event);//到这里就是我们开始需要的入口点了,终于找到了 不容易
}
至此,我们终于知道onInputEvent是被谁一步步回调的了。
这里我们先不追踪刚才那个被监听的fd从哪里来,先对以上过程做一个总结:
1.Activity初始化的过程中,ActivityThread会通过android.view.WindowManagerImpl类为该Activity创建一个ViewRoot实例,并且会通过调用ViewRoot类的setView成员函数把与该Activity关联的View设置到这个ViewRoot中去,而setView逻辑中会初始化一个InputEventReceiver作为input事件的客户端接受者,InputEventReceiver初始化的过程会在c++层初始化一个NativeInputEventReceiver对象,而在NativeInputEventReceiver初始化的时候,会通过传递来的Looper(客户端主线程)监听一个输入事件的fd(epoll监听机制),当输入事件发生的时候会回调handleEvent方法,最终回调到InputEventReceiver的onInputEvent方法将事件传递给java层。
2.InputEventReceiver的onInputEvent方法被回调之后,会首先将事件传给InputStage责任链,这里面会判断要不要传递给输入法服务。如果只是motionEvent,就会首先传递给PhoneWindow的DecorView,DecorView会把这个事件传给phonewindow的callback也就是当前activity,activity会首先把事件再次传递给phoneWindow,接着phoneWindow就让事件在以DecorView为根的viewGroup中层层传递,直到事件最终被view处理。当然如果我们重写activity的dispatchTouchEvent方法,事件就到不了viewgroup中了。
三、输入事件Fd究竟是何方神圣
在上一步中,
int fd = mInputConsumer.getChannel()->getFd();
mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
我们知道这里的fd是关键,为什么监听这个fd就可以拿到输入事件呢?为了解决这个疑问,我们再次采用追本溯源的方式,一步步找到这个fd的源头。经过追踪,发现源头还是在ViewRootImpl的setView方法中:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
.................................
mInputChannel = new InputChannel();
......................
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());
..........................
}
很明显,在上一步中我们已经知道这是InputEventReceiver的初始化源头,之后会进入InputEventReceiver的native层的初始化流程,并最终通过Looper监听fd来最终监听输入事件。而这里的mInputChannel很明显就是int fd = mInputConsumer.getChannel()->getFd()中我们要找的InputChannel. ok,这里这个InputChannel的源头我们找到了,但是更大的疑问来了,明明这里的mInputChannel只是一个简单的初始化,并没有监听什么端口之类的,为什么能在它哪里监听到输入事件呢?只能是之后我们对mInputChannel又做了某些赋值处理,所以,我们继续追踪setView这个方法,果不其然发现了
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(),
mDisplay.getDisplayId(),mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);
这里mWindowSession是WindowManagerService的远程代理,最终实际上会调到WindowManagerService的addWindow方法中去,而在这个方法里就有对mInputChannel进行真正赋值的操作,下面我们来看addWindow方法的逻辑:
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, InputChannel outInputChannel) {
................
1 String name = win.makeInputChannelName();
2 InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
3 win.setInputChannel(inputChannels[0]);
4 inputChannels[1].transferTo(outInputChannel);
5 mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
..............................
}
这里简单说明一下:
我们先来分析InputChannel.openInputChannelPair的过程:
最终调用到Jni层的android_view_InputChannel_nativeOpenInputChannelPair
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
String8 name(nameChars);
env->ReleaseStringUTFChars(nameObj, nameChars);
sp serverChannel;
sp clientChannel;
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(serverChannel));
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(clientChannel));
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;
}
这里调用InputChannel::openInputChannelPair创建了server端和client端的通道,然后调用 android_view_InputChannel_createInputChannel将native层InputChannel对象转换为Java层InputChannel对象,我们看一下InputChannel::openInputChannelPair的具体实现
status_t InputChannel::openInputChannelPair(const String8& name,
sp& outServerChannel, sp& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
status_t result = -errno;
ALOGE("channel '%s' ~ Could not create socket pair. errno=%d",
name.string(), errno);
outServerChannel.clear();
outClientChannel.clear();
return result;
}
int bufferSize = SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
String8 serverChannelName = name;
serverChannelName.append(" (server)");
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
String8 clientChannelName = name;
clientChannelName.append(" (client)");
outClientChannel= new InputChannel(clientChannelName, sockets[1]);
return OK;
}
这里我们发现建立了一对匿名的已经连接的套接字socket,同时outServerChannel和outClientChannel分别持有
这对套接字的两端,这样就实现了server端写入输入事件能被client端读到。到这里我们就明白了client端对
输入事件的监听最终是监听socket。
再看另一个问题,就是之前WindowManagerService.addWindwo方法中:
inputChannels[1].transferTo(outInputChannel);这实际上是将生成的outClientChannel赋值给outInputChannel,但这里并没有赋值给client端的InputChannel啊?这里到底是怎么影响到client端的InputChannel呢?
这里实际上是利用binder调用中out关键字,我们来看一下IWindowSession.aidl中的addToDisplay方法声明:
int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outContentInsets,
out Rect outStableInsets, out InputChannel outInputChannel);
很明显这里使用了out关键字,对binder调用熟悉的同学就知道实际上这个关键字的作用就是我们传递给server端的参数在server进程中被改变的话会被反馈回给client端。这样就相当于我们在client端可以拿到之前打开的那个socket,最终回到第二部分,就可以被Looper监听这个socket了。
这里面大致上就把client端的InputChannel的赋值过程给介绍了。这里面还有一个小细节值得介绍一下:
就是我们看到InputChannel.java中有这样一个成员变量:
@SuppressWarnings("unused")
private long mPtr; // used by native code
这里实际上已经注释的很清楚了,就是mPtr是用于java层InputChannel对象和native层的InputChannel对象转换用的,我们来看一下android_view_InputChannel.cpp中将java对象转换为nativie对象的方法
static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
jobject inputChannelObj) {
jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);
return reinterpret_cast(longPtr);
}
这里我们可以看到实际上mPtr变量就是InputChannel对应的native对象的指针,只需要一个强制类型转换就可以拿到NativeInputChannel*了,是很方便的。这种java变量和native变量的变换在整个android系统中是很普通的。
static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject inputChannelObj,
NativeInputChannel* nativeInputChannel) {
env->SetLongField(inputChannelObj, gInputChannelClassInfo.mPtr,
reinterpret_cast(nativeInputChannel));
}
四、server端对输入事件的监听
在第三部分分析中,我们知道会创建一对持有连接好的socket的InputChannel分别交给client端和server端,server端对socket写入事件时,会被client端监听到从而将输入事件传递给上层IME输入法或View、Activity.那么现在问题来了,server端是如何获取到输入事件的呢,又是在哪里往这个socket里写入事件的呢?这就来到了input子系统最复杂的一块了,InputManagerService的初始化过程、以及对输入事件的轮询和分发过程。这里面我们分两部分讲解:
1.InputManagerService的初始化过程
这是InputManagerService的初始化流程,最终InputManager中会初始化两个thread,InputReaderThread和DispatcherThread。这两个thread会loop起来,其中InputReaderThread会wait在EventHub->getEvents这里等待下面上报输入事件;而DispatcherThread会mLooper->pollOnce(),睡眠在这里,等待被InputReaderThread唤醒mLooper->wake().
EventHub是输入设备的控制中心,它直接与input driver打交道。负责处理输入设备的增减,查询,输入事件的处理并向上层提供getEvents()接口接收事件。在它的构造函数中,主要做三件事:
1. 创建epoll对象,之后就可以把各输入设备的fd挂在上面多路等待输入事件。
2. 建立用于唤醒的pipe,把读端挂到epoll上,以后如果有设备参数的变化需要处理,而getEvents()又阻塞在设备上,就可以调用wake()在pipe的写端写入,就可以让线程从等待中返回。
3. 利用inotify机制监听/dev/input目录下的变更,如有则意味着设备的变化,需要处理。
2.Server端对输入事件的轮询和分发过程
首先通过EventHub的getEvents函数读取输入设备事件,经过处理后从事件中获得对应的deviceId,根据deviceId在mDevices数组中找到对应的输入设备对象或者构造新的InputDevice对象,然后调用InputDevice的process函数。
在InputDevice的process函数中对每个事件由InputDevice对象的mMappers数组中登记的每个InputMapper对象依次处理,分别调用其process函数,对原始输入事件进行映射。
不同的事件类型采用了不同的具体InputMapper对象进行映射,如按键事件对应的是KeyboadInputMapper对象,另外还有TouchInputMapper、CursorInputMapper、VibratorInputMapper、SwitchInputMapper、JoystickInputMapper等映射对象,这些对象的类都是InputMapper虚拟类的具体类。
一个支持多种类型的事件输入的输入设备,需要使用多个不同的输入映射对象分别进行映射。
在InputMapper对象的process函数中把事件封装成NotifyArgs对象,然后调用InputMapper的监听对象QueedInputListener的相关事件监听接口函数,如notifyKey函数。
在QueedInputListener的监听接口函数中作为参数传进来的NotifyArgs对象放入QueedInputListener的事件队列ArgsQueue中,然后返回并在loopOnce函数中调用QueuedListener对象的flush函数。在flush函数中依次调用ArgsQueue队列中由事件封装成的NotifyArgs对象的notify函数,notify函数的参数也是一个监听对象,在QueuedInputListener对象实例化时赋值,对应的是一个InputDispatcher对象。这里面的QueuedInputListener是在InputReader初始化时赋值的,同样InputDispatcher作为QueuedInputListener的构造参数也是这时候传进来的。NotifyArgs对象的notify函数调用其参数引用对象(InputDispatcher)的事件通知函数,以NotifyArgs对象为参数,如按键事件对应的notifyKey。
InputDispatcher对象的事件通知函数根据作为参数传进来的事件构造一个EventEntry对象,然后调用enqueueInboundEventLocked函数把构造的EventEntry事件对象放入内部事件队列中(mInboundQueue),最后调用Looper的wake函数,唤醒事件提交线程即InputDispatcherThread线程来进行事件向管道中的提交。
InputReader读取的输入事件作为参数通过InputListenerInterface的接口函数被InputDispatcher接收。接收的事件构造为EventEntry对象放入InputDispatcher对象的EventEntry类型的队列mInboundQueue中,并唤醒InputDispatcherThread线程。InputDispatcherThread线程不断从该队列中读取输入事件。首先调用InputDispatcher对象的dispatchOnce函数;dispatchOnce函数又调用dispatchOnceInnerLocked函数,在dispatchOnceInnerLocked函数中进行一系列判断后若是按键事件则调用dispatchKeyLocked函数;在dispatchKeyLocked函数中使用findFocusedWindowTargetsLocked函数寻找焦点窗口,并把找到的焦点窗口通过调用函数addWindowTargetLocked放入mCurrentInputTargets数组中,然后调用addMonitoringTargetsLocked为刚才找到的焦点窗口绑定一个inputChannel通道,接着调用dispatchEventLocked函数;在dispatchEventLocked函数中首先根据刚才焦点窗口绑定的inputChannel找到对应的一个Connection对象,然后调用prepareDispatchCycleLocked;在prepareDispatchCycleLocked函数中调用enqueueDispatchEntryLocked函数,在enqueueDispatchEntryLocked函数中根据传进来的事件对应的对象eventEntry构造一个 DispatchEntry对象,DispatchEntry对象中包含inputTarget的信息,并放入connection对象outboundQueue队列;接着又调用startDispatchCycleLocked;
在startDispatchCycleLocked函数中通过connection对象中的inputPublisher对象引用调用inputPublisher对象的publishKeyEvent函数或publishMotionEvent函数向客户端发送事件,最终通过InputChannel中socket写给client处理,这就解决了第三部分提出的问题。
anr流程
int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
const EventEntry* entry,
const sp& applicationHandle,
const sp& windowHandle,
nsecs_t* nextWakeupTime, const char* reason) {
.................................
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
mInputTargetWaitTimeoutTime = currentTime + timeout;
}
.......................
if (currentTime >= mInputTargetWaitTimeoutTime) {
onANRLocked(currentTime, applicationHandle, windowHandle,
entry->eventTime, mInputTargetWaitStartTime, reason);
// Force poll loop to wake up immediately on next iteration once we get the
// ANR response back from the policy.
*nextWakeupTime = LONG_LONG_MIN;
return INPUT_EVENT_INJECTION_PENDING;
} else {
// Force poll loop to wake up when timeout is due.
if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
*nextWakeupTime = mInputTargetWaitTimeoutTime;
}
return INPUT_EVENT_INJECTION_PENDING;
}
}
按键事件的拦截
按键事件的拦截请求包括队列前的事件拦截(譬如keycode_power)和提交前的拦截(譬如keycode_home),下面以按键事件的拦截为例进行说明。
对于队列前的按键事件拦截请求,在InputDispatche对象的notifyKey函数和injectInputEvent函数提交输入按键事件到队列之前,通过调用InputDispatcherPolicyInterface接口对象的interceptKeyBeforeQueueing函数发送事件的队列之前拦截请求;而对于事件提交前的按键事件拦截通过调用InputDispatcherPolicyInterface接口对象的interceptKeyBeforeDispatching回调函数来发送其拦截请求,其流程如下:
在InputDispatche对象的事件提交函数dispatchKeyLocked中判断事件的policyFlags是否含有POLICY_FLAG_PASS_TO_USER,如果含有POLICY_FLAG_PASS_TO_USER就调用InputDispatche对象的postCommandLocked函数,postCommandLocked的参数为doInterceptKeyBeforeDispatchingLockedInterruptible函数指针。在postCommandLocked函数中构造一个CommandEntry对象,其command方法为参数传进来的函数指针,并放入mCommandQueue队列,然后函数dispatchKeyLocked返回,事件在事件提交之前被拦截不再继续派发。
在函数dispatchKeyLocked返回到dispatchOnce,继续调用runCommandsLockedInterruptible函数对mCommandQueue队列中的命令对象进行处理,对于mCommandQueue队列中的每一个命令对象执行命令对象的command方法,对于刚才放入的命令实际调用的是作为postCommandLocke参数的InputDispatch:doInterceptKeyBeforeDispatchingLockedInterruptible函数指针;因此doInterceptKeyBeforeDispatchingLockedInterrup
tible函数被调用,在doInterceptKeyBeforeDispatchingLockedInterruptible函数中调用InputDispatcherPolicyInterface接口对象的interceptKeyBeforeDispatching事件拦截回调函数发送事件提交前的拦截请求。
InputDispatcherPolicyInterface接口对象在InputDispatcher对象构造时把对NativeInputManager对象的引用作为参数传入赋值给InputDispatcher对象的InputDispatcherPolicyInterface接口类型的字段mPolicy,因此上述的对InputDispatcherPolicyInterface接口事件的调用实际调用的是NativeInputManager对象的相应函数。
而NativeInputManage对象的拦截和过滤回调函数又通过JNI调用JAVA层InputManagerService服务的相应函数,如interceptKeyBeforeDispatching、interceptKeyBeforeQueueing和filterInputEvent函数。
InputManagerService服务的事件拦截函数实际调用的是WindowManagerCallbacks接口的相应接口函数,而InputMonitor对象是WindowManagerCallbacks接口的实现,因此对WindowManagerCallbacks接口函数的调用就是对InputMonitor对象的相应函数的调用。InputMonitor对象的对应函数又通过WindowManagerService窗口管理服务中的PhoneWindowManager对象引用调用PhoneWindowManager对象的对应函数,最终完成事件的拦截处理。
在client处理完事件之后也是要通知server端的,这也是通过Looper::addFd实现监听的
好了,以上就是整个分析过程。最后我们给出input子系统整个clinet端和server端的关系图