系列目录 :第一篇:从硬件到应用的事件旅程 | 第二篇:EventHub --- 原始事件的采集者 | 第三篇:InputReader --- 原始事件到Android事件的转换引擎 | 第四篇:InputDispatcher --- 事件分发与ANR超时机制 | 第五篇:应用侧 --- InputChannel、ViewRootImpl与事件消费
一、应用侧在整体架构中的位置
InputDispatcher → socket → APP 进程 → View 树
system_server ▲
本篇聚焦
当 InputDispatcher 通过 socket 把 InputMessage 发送到 APP 进程后,一条完整的消费链路开始运转。本篇聚焦于:socket 数据如何被 APP 进程感知,如何转换为 Java 层的 InputEvent 对象,如何与主线程消息队列协作,以及最终如何在 View 树中分发。
源码位置:
frameworks/base/core/java/android/view/ViewRootImpl.java // WindowInputEventReceiver
frameworks/base/core/java/android/view/InputChannel.java // socket pair 通信
frameworks/base/core/java/android/view/InputEventReceiver.java // 事件接收基类
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java // WMS
frameworks/base/core/jni/android_view_InputEventReceiver.cpp // JNI 桥接层
二、InputChannel 的创建
2.1 ViewRootImpl.setView() 触发通道创建
java
// ViewRootImpl.java
public void setView(View view, ...) {
mView = view;
// WMS 在 addWindow 时调用 openInputChannelPair()
// server 端 → InputDispatcher,client 端 → 通过 Binder 回传
res = mWindowSession.addToDisplay(mWindow, ...);
if (mInputChannel != null) {
mInputEventReceiver = new WindowInputEventReceiver(
mInputChannel, Looper.myLooper());
}
}
2.2 WMS 侧创建 socket pair
java
// WindowManagerService.java
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
// server 端 → 注册到 InputDispatcher
mInputManager.registerInputChannel(inputChannels[0], ...);
// client 端 → 通过 Binder 回传给 ViewRootImpl
outInputChannel = inputChannels[1];
三、NativeInputEventReceiver:socket fd 与 MessageQueue 集成
3.1 JNI 初始化
cpp
// android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
sp<InputChannel> inputChannel = android_view_InputChannel::getInputChannel(env, inputChannelObj);
sp<MessageQueue> messageQueue = android_os_MessageQueue::getMessageQueue(env, messageQueueObj);
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env, receiverWeak, inputChannel, messageQueue);
receiver->initialize();
return reinterpret_cast<jlong>(receiver.get());
}
3.2 核心:将 socket fd 注册到 MessageQueue 的 Looper
cpp
void NativeInputEventReceiver::setFdEvents(int events) {
int fd = mInputConsumer.getChannel()->getFd(); // socket 的 fd
// 将 socket fd 加入 MessageQueue 的 Native Looper 的 epoll 实例
mMessageQueue->getLooper()->addFd(
fd,
0, // ident
ALOOPER_EVENT_INPUT, // 监听可读事件
this, // 回调对象(handleEvent 方法)
NULL
);
}
这是整个应用侧最精妙的设计:
- MessageQueue 内部使用 Native Looper + epoll 等待消息
- 同一个 epoll 实例同时监听:主线程 Message 管道 + socket fd
- 当 InputDispatcher 向 socket
send()数据时,epoll_wait同时检测到 socket fd 可读 - 在主线程上下文中回调
NativeInputEventReceiver::handleEvent()
这意味着输入事件和主线程的 Message 共享同一个事件循环。如果主线程在处理耗时 Message,输入事件的处理会被延迟------这就是"主线程卡顿导致触摸延迟"的根本原因。
3.3 事件消费与回调 Java 层
cpp
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
if (events & ALOOPER_EVENT_INPUT) {
consumeEvents(env, false, -1, NULL);
}
return 1;
}
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, ...) {
for (;;) {
InputEvent* inputEvent;
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &inputEvent);
if (status == OK) {
// 回调 Java 层的 dispatchInputEvent()
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent,
seq, inputEventObj);
} else if (status == WOULD_BLOCK) {
break; // 没有更多事件
}
}
}
3.4 事件消费回复(Finished Signal)
cpp
status_t NativeInputEventReceiver::sendFinishedSignal(uint32_t seq, bool handled) {
// 通过同一 socket 发送 Finished 消息给 InputDispatcher
return mInputConsumer.sendFinishedSignal(seq, handled);
}
APP 必须调用 sendFinishedSignal 来告知 InputDispatcher 事件已被消费。如果长时间不调用,就会触发 ANR 超时。
四、InputEventReceiver(Java 层基类)
java
public abstract class InputEventReceiver {
private long mReceiverPtr; // NativeInputEventReceiver 指针
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
mInputChannel = inputChannel;
mReceiverPtr = nativeInit(new WeakReference<>(this),
inputChannel, looper.getQueue());
}
// JNI 回调此方法
private void dispatchInputEvent(int seq, InputEvent event) {
onInputEvent(event);
}
public abstract void onInputEvent(InputEvent event);
// 通知 Dispatcher 事件已消费
public final void finishInputEvent(InputEvent event, boolean handled) {
nativeFinishInputEvent(mReceiverPtr, seq, handled);
}
}
五、ViewRootImpl:窗口事件中枢
5.1 WindowInputEventReceiver
java
final class WindowInputEventReceiver extends InputEventReceiver {
@Override
public void onInputEvent(InputEvent event) {
enqueueInputEvent(event, this, 0, true);
}
}
5.2 事件入队与处理
java
void enqueueInputEvent(InputEvent event, InputEventReceiver receiver,
int flags, boolean processImmediately) {
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
// 加入队列尾
if (mPendingInputEventTail == null) {
mPendingInputEventHead = q;
} else {
mPendingInputEventTail.mNext = q;
}
mPendingInputEventTail = q;
if (processImmediately) {
doProcessInputEvents();
} else {
scheduleProcessInputEvents(); // 调度到下一帧
}
}
void doProcessInputEvents() {
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
deliverInputEvent(q);
}
}
5.3 InputStage 责任链流水线
ViewRootImpl 用责任链模式处理输入事件,依次经过 6 个 Stage:
java
// Stage 链顺序:
abstract class InputStage {
// 1. NativePreImeInputStage → 原生输入法之前处理
// 2. ImeInputStage → 输入法(IME)处理
// 3. EarlyPostImeInputStage → 输入法之后、View 之前
// 4. ViewPreImeInputStage → View 分发输入法相关事件
// 5. ViewPostImeInputStage → View 分发普通事件(核心!)
// 6. SyntheticInputStage → 合成事件(导航键等)
}
最核心的 ViewPostImeInputStage:
java
final class ViewPostImeInputStage extends InputStage {
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
} else {
return processPointerEvent(q);
}
}
private int processPointerEvent(QueuedInputEvent q) {
MotionEvent event = (MotionEvent) q.mEvent;
boolean handled = mView.dispatchPointerEvent(event);
// → DecorView.dispatchTouchEvent(event)
return handled ? FINISH_HANDLED : FORWARD;
}
private int processKeyEvent(QueuedInputEvent q) {
KeyEvent event = (KeyEvent) q.mEvent;
boolean handled = mView.dispatchKeyEvent(event);
return handled ? FINISH_HANDLED : FORWARD;
}
}
六、View 树的事件分发
6.1 触摸事件分发(ViewGroup)
java
// ViewGroup.java
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean handled = false;
// 1. 检查是否被拦截
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
intercepted = onInterceptTouchEvent(ev);
} else {
intercepted = true;
}
// 2. 不拦截则分发给子 View
if (!intercepted) {
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
// 按 Z 序从上层到下层
View child = children[i];
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
continue;
}
// 坐标转换到子 View 坐标系 + 递归分发
ev.offsetLocation(child.mLeft, child.mTop);
if (child.dispatchTouchEvent(ev)) {
mFirstTouchTarget = addTouchTarget(child, ...);
handled = true;
break;
}
}
}
// 3. 没有子 View 消费,自己处理
if (mFirstTouchTarget == null) {
handled = super.dispatchTouchEvent(ev); // → View.onTouchEvent()
}
return handled;
}
6.2 View 的事件处理
java
// View.java
public boolean dispatchTouchEvent(MotionEvent event) {
// 1. 先回调 OnTouchListener
if (mOnTouchListener != null && mOnTouchListener.onTouch(this, event)) {
return true;
}
// 2. 再回调 onTouchEvent
if (onTouchEvent(event)) {
return true;
}
return false;
}
public boolean onTouchEvent(MotionEvent event) {
if ((viewFlags & CLICKABLE) == CLICKABLE) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
performClick(); // 执行点击
break;
case MotionEvent.ACTION_DOWN:
checkForLongClick(0); // 延迟长按检测
break;
}
return true;
}
return false;
}
6.3 按键事件分发
按键事件优先交给焦点 View:
java
// ViewGroup.java
public boolean dispatchKeyEvent(KeyEvent event) {
if (mFocused != null) {
return mFocused.dispatchKeyEvent(event);
}
return super.dispatchKeyEvent(event);
}
// View.java
public boolean dispatchKeyEvent(KeyEvent event) {
// 1. 回调 OnKeyListener
if (mOnKeyListener != null && mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
return true;
}
// 2. 回调 onKeyDown / onKeyUp
return event.dispatch(this, ...);
}
七、消费信号回传闭环
java
// ViewRootImpl.java
private void finishInputEvent(QueuedInputEvent q) {
if (q.mReceiver != null) {
boolean handled = (q.mFlags & FLAG_FINISHED_HANDLED) != 0;
q.mReceiver.finishInputEvent(q.mEvent, handled);
}
}
// → InputEventReceiver.finishInputEvent()
// → JNI nativeFinishInputEvent()
// → NativeInputEventReceiver::sendFinishedSignal(seq, handled)
// → mInputConsumer.sendFinishedSignal()
// → socket send(FinishedMessage)
// → InputDispatcher 的 fd 变为可读
// → handleReceiveCallback() → 从 waitQueue 移除 → ANR 计时器停止
八、关键设计总结
| 设计 | 说明 |
|---|---|
| socket pair | 输入通道不经过 Binder,降低延迟 |
| fd 注册到 MessageQueue epoll | 输入事件与主线程 Message 共享同一事件循环 |
| NativeInputEventReceiver | JNI 层桥接,读 socket + 回调 Java |
| InputStage 责任链 | IME 前置 → View 分发 → 合成事件,6 级流水线 |
| ViewGroup 拦截机制 | onInterceptTouchEvent + mFirstTouchTarget |
| FINISHED 信号闭环 | APP 消费 → socket 回复 → InputDispatcher 移出 waitQueue → 阻止 ANR |
系列总结
五篇文章涵盖了 Android 7 输入系统从硬件到应用的完整链路:
- 总览:建立六层架构的宏观认知
- EventHub:inotify + epoll 的设备监听与原始事件采集
- InputReader:InputMapper 体系与事件加工转换
- InputDispatcher:窗口定位、socket 通信与 ANR 超时机制
- 应用侧:InputChannel 到 View.onTouchEvent 的完整消费链路
每篇文章都是独立的知识模块,可以按需查阅。理解这套机制后,无论是排查 ANR、处理滑动冲突、还是优化触摸响应延迟,都能从源码层面找到根本原因。