Android 事件传递机制

Android中的事件包括两类,Touch触摸事件Key按键事件,我们都知道事件会先传递给Activity,但事件不是凭空产生的,那么它是从哪里传递到Activity的呢?

Android中的事件,在底层涉及到InputReaderInputDispatcherInputManagerEventHub。在上层涉及到WindowActivityViewGroupView
它们是怎么传递的呢?传递则涉及到InputChannelInputEventReceiver

Source

驱动层:

1
2
3
4
5
/frameworks/native/services/inputflinger/
InputReader.cpp (Thread)
InputDispatcher.cpp (Thread)
InputManager.cpp
EventHub.cpp

中间传输层:

1
2
3
/frameworks/base/core/jni/
android_view_InputChannel.cpp
android_view_InputEventReceiver.cpp

应用层:
dispatchTouchEvent下行流程:

1
Activity -> ViewGroup -> ViewGroup.onInterceptTouchEvent -> View -> View.onTouchEvent -> ViewGroup.onTouchEvent;

InputManagerService创建

InputManagerService的创建是在SystemServer#startOtherServices中,直接通过new InputManagerService()创建实例对象,然后调用IMS#nativeInit()方法来对底层进行初始化。

1
2
3
4
5
6
7
8
9
10
SystemServer#startOtherServices {
ims = new InputManagerService() {
nativeInit(this, mContext, mHandler.getLooper().getQueue());
LocalServices.addService(InputManagerInternal.class, new LocalService());
}
ims.setWindowManagerCallbacks(wm.getInputMonitor());
ims.start();
}

InputManagerService构造函数中的主要工作

  1. 创建一个InputDispatcher对象用于分发事件
  2. 创建一个InputReader对象用于读事件并把事件交给InputDispatcher分发
  3. 调用initialize()初始化,其实也就是创建了InputReaderThreadInputDispatcherThread

事件传递

使用InputEventReceiverInputChannel传递事件。

  1. ViewRootImpl#setView中创建InputEventReceiver
1
2
3
4
5
6
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
// InputEventReceiver
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());

InputEventReceiver.java

1
2
3
4
5
6
7
8
9
10
// Called from native code.
@SuppressWarnings("unused")
private void dispatchInputEvent(int seq, InputEvent event, int displayId) {
// ...
onInputEvent(event, displayId);
}
public void onInputEvent(InputEvent event, int displayId) {
finishInputEvent(event, false);
}

底层android_view_InputEventReceiver.cpp中有个for循环,如果捕获到输入事件,则通过Java层dispatchInputEvent方法的methodId,调用到Java层的dispatchInputEvent方法,这样就将事件从native层传递到了Java层。

InputEventReceiver接收到的事件,并不是InputDispatcher直接调用到了InputEventReceiver的方法,从上面的创建过程可以得知,InputEventReceiver是运行于应用程序进程内的,而事件是产生在系统进程的,这中间涉及到了进程通信,实际是它是使用InputChannel来通信的。

InputChannel

Linux本地socket,可以在进程间进行通信。

设置FocusWindow

事件从System_Process中,传递到应用程序进程中,怎么对应上需要接收事件的Window呢,这就是需要了解FocusWindow了。

1
2
3
4
WMS.addWindow
-> WMS.mInputMonitor.updateInputWindowsLw(false /*force*/);
-> IMS.updateInputWindows
-> IMS.setInputWindows

InputManagerService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
public void setInputWindows(InputWindowHandle[] windowHandles,
InputWindowHandle focusedWindowHandle) {
final IWindow newFocusedWindow =
focusedWindowHandle != null ? focusedWindowHandle.clientWindow : null;
if (mFocusedWindow != newFocusedWindow) {
mFocusedWindow = newFocusedWindow;
if (mFocusedWindowHasCapture) {
setPointerCapture(false);
}
}
// 设置本地窗口
nativeSetInputWindows(mPtr, windowHandles);
}