Skip to content

输入事件派发路径

从 InputManagerService 的 native 启动到 InputChannel、InputEventReceiver 和 ViewRootImpl Handler,追踪输入事件的接收、入队、确认与清理。

基于android-17.0.0_r1
AndroidInputManagerServiceInputEventReceiverViewRootImpl源码阅读

输入事件派发路径 ​

输入事件从 system_server 到应用 View,并不是由一个 Java Handler 贯穿。Android 17 的实际 边界是:Java InputManagerService 启动 native InputManager;应用侧 InputEventReceiver 把 InputChannel 文件描述符注册到目标 Looper 的 native Looper;native 回调解析事件并调用 Java 私有 dispatchInputEvent();ViewRootImpl 再把事件放进自己的队列,必要时发送异步 MSG_PROCESS_INPUT_EVENTS,最终沿 InputStage 链派发并通过 finishInputEvent() 回写确认。

本文面向已经读过 ActivityThread.H 主线程、 ViewRootHandler 绘制路径、异步 Handler 工厂 和 Native Looper addFd 的读者。文章只追踪 “已有 InputChannel 的窗口收到一个事件”这条路径,不展开 InputReader 的设备建模、 InputDispatcher 的窗口选择算法或完整 ANR 判定策略。

读完后,读者应能定位 Java Handler 真正介入的时点,解释为什么 InputChannel FD 要挂到 MessageQueue 对应的 native Looper,区分 InputEventReceiver 的 native 消费与 ViewRootImpl 的 Java 队列,并判断事件未确认、receiver dispose 或队列退出时会发生什么。

1. 服务边界 ​

源码文件:frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

InputManagerService.start() 的 Java 职责是调用 native 实现的 start(),再把自身加入 Watchdog;它不在 Java Handler 中逐个消费触摸事件。

java
public void start() {
    Slog.i(TAG, "Starting input manager");
    mNative.start();
    Watchdog.getInstance().addMonitor(this);
}

源码文件:frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

JNI nativeInit() 读取传入的 Java MessageQueue,取得其 native Looper,用它构造长期存在的 NativeInputManager;nativeStart() 再启动 native InputManager。

cpp
static jlong nativeInit(JNIEnv* env, jclass, jobject serviceObj,
        jobject messageQueueObj, bool createInteractionReporter) {
    sp<MessageQueue> messageQueue =
            android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == nullptr) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
    NativeInputManager* im = new NativeInputManager(
            serviceObj, messageQueue->getLooper(), createInteractionReporter);
    return reinterpret_cast<jlong>(im);
}

static void nativeStart(JNIEnv* env, jobject nativeImplObj) {
    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
    status_t result = im->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}

这里的 MessageQueue 是 system_server 侧用于 native 回调/策略通知的 Looper 绑定,不等于 应用窗口接收输入事件的 MessageQueue。窗口事件真正进入应用进程后,会重新在应用的 InputEventReceiver 上注册另一条 InputChannel FD。

2. Native 线程 ​

源码文件:frameworks/native/services/inputflinger/InputManager.cpp

native InputManager::start() 先启动 InputDispatcher,再启动 InputReader;如果 Reader 启动 失败,会停止已经启动的 Dispatcher。这里是 native 线程生命周期,不是 Java Handler 的消息 循环。

cpp
status_t InputManager::start() {
    status_t result = mDispatcher->start();
    if (result) {
        ALOGE("Could not start InputDispatcher thread due to error %d.", result);
        return result;
    }

    result = mReader->start();
    if (result) {
        ALOGE("Could not start InputReader thread due to error %d.", result);
        mDispatcher->stop();
        return result;
    }
    return OK;
}

这两个线程把事件送到目标窗口的 InputChannel;应用 Handler 只在事件已经跨过 channel、 进入应用 Looper 后介入。把 InputReader/Dispatcher 说成“由 Java Handler 驱动”会混淆 system_server native 层和应用 Java 层的所有权。

3. Receiver 建立 ​

源码文件:frameworks/base/core/java/android/view/InputEventReceiver.java

InputEventReceiver 构造函数要求非空 InputChannel 和 Looper,保存 Looper 防止 native peer 使用的对象过早回收,然后把 Java 对象弱引用、InputChannel 和 MessageQueue 传给 native。

java
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
    if (inputChannel == null) {
        throw new IllegalArgumentException("inputChannel must not be null");
    }
    if (looper == null) {
        throw new IllegalArgumentException("looper must not be null");
    }

    mLooper = looper;
    mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
            inputChannel, mLooper.getQueue());
    mCloseGuard.open("InputEventReceiver.dispose");
}

对象关系是:Java receiver 拥有 native 指针和 Looper 引用;native receiver 拥有 InputConsumer、 InputChannel 和 Java 弱引用;Looper 拥有 FD 回调注册。任意一方 dispose 都必须考虑其它方仍 可能持有的引用。

4. FD 注册 ​

源码文件:frameworks/base/core/jni/android_view_InputEventReceiver.cpp

native NativeInputEventReceiver::initialize() 把 InputChannel 的 FD 注册到传入 MessageQueue 对应的 native Looper。事件可读时,Looper 调用 handleEvent()。

cpp
status_t NativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}

void NativeInputEventReceiver::setFdEvents(int events) {
    if (mInputConsumer == nullptr) {
        return;
    }
    const int fd = mInputConsumer->getChannel()->getFd();
    if (events) {
        mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);
    } else {
        mMessageQueue->getLooper()->removeFd(fd);
    }
    mFdEvents = events;
}

这里没有 Java Handler.post()。native Looper 的 FD 事件是 MessageQueue nativePollOnce() 的一部分;它唤醒的仍是应用 Looper 线程,随后才执行 native callback。

图中的 Java Handler 消息尚未出现;native FD callback 是事件进入应用 Looper 的第一跳。

5. Native 消费 ​

源码文件:frameworks/base/core/jni/android_view_InputEventReceiver.cpp

handleEvent() 根据 poll 事件决定继续保留还是移除 FD callback;输入事件可读时调用 consumeEvents(),异常或不可恢复状态会影响 callback 是否继续注册。

cpp
int NativeInputEventReceiver::handleEvent(int, int events, void*) {
    constexpr int REMOVE_CALLBACK = 0;
    constexpr int KEEP_CALLBACK = 1;

    if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
        return REMOVE_CALLBACK;
    }
    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        status_t status = consumeEvents(env, false, -1, nullptr);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK;
    }
    return KEEP_CALLBACK;
}

consumeEvents() 通过 InputConsumer 从 channel 解码 Key/Motion 等输入,再调用 Java 私有 dispatchInputEvent(seq, event)。如果 Java receiver 已被回收,native 弱引用取不到对象时 返回 DEAD_OBJECT;如果 Java 回调抛异常,native 会停止继续调用本轮 callback。

cpp
env->CallVoidMethod(receiverObj.get(),
        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj.get());
if (env->ExceptionCheck()) {
    ALOGE("Exception dispatching input event.");
    skipCallbacks = true;
}

6. Java 序号 ​

源码文件:frameworks/base/core/java/android/view/InputEventReceiver.java

native 回调进入私有 dispatchInputEvent() 后,Java 先把 event sequence 映射到 dispatcher sequence,再调用可覆写的 onInputEvent()。这个 map 是后续 finishInputEvent() 找回 ACK 序号的依据。

java
private void dispatchInputEvent(int seq, InputEvent event) {
    mSeqMap.put(event.getSequenceNumber(), seq);
    onInputEvent(event);
}

默认 onInputEvent() 立即以未处理状态结束事件;ViewRootImpl 的子类会覆盖它,把事件放入 自己的输入队列。这里的“Handler 派发”由消费者决定,而不是 InputEventReceiver 基类自动 发送 Message。

7. ViewRoot 接收 ​

源码文件:frameworks/base/core/java/android/view/ViewRootImpl.java

ViewRootImpl 的 WindowInputEventReceiver 覆盖 onInputEvent(),调用 processRawInputEvent();事件随后进入 enqueueInputEvent()。该队列按接收顺序保存,不按 事件时间戳重新排序。

java
final class WindowInputEventReceiver extends InputEventReceiver {
    @Override
    public void onInputEvent(InputEvent event) {
        processRawInputEvent(event);
    }
}

QueuedInputEvent enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
    QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
    // 追加到 mPendingInputEventHead/mPendingInputEventTail
    mPendingInputEventCount += 1;
    if (processImmediately) {
        doProcessInputEvents();
    } else {
        scheduleProcessInputEvents();
    }
    return q;
}

mPendingInputEvent* 是 ViewRootImpl 自己的队列,不是 Java MessageQueue。这样可以在一次 处理阶段连续消费多个事件;只有 scheduleProcessInputEvents() 才把“需要处理”转成 ViewRootHandler 消息。

8. Handler 转发 ​

源码文件:frameworks/base/core/java/android/view/ViewRootImpl.java

当 processImmediately 为 false,ViewRootImpl 用 mProcessInputEventsScheduled 去重,发送 异步 MSG_PROCESS_INPUT_EVENTS。异步位让输入处理消息在 traversal barrier 存在时仍具备出队 资格,但不能抢占当前正在运行的 callback。

java
private void scheduleProcessInputEvents() {
    if (!mProcessInputEventsScheduled) {
        mProcessInputEventsScheduled = true;
        Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS);
        msg.setAsynchronous(true);
        mHandler.sendMessage(msg);
    }
}

消息到达后只负责把 ViewRoot 的 pending 队列交给处理循环:

java
case MSG_PROCESS_INPUT_EVENTS:
    mProcessInputEventsScheduled = false;
    doProcessInputEvents();
    break;

消息只是一条“开始处理 ViewRoot 输入队列”的信号;事件对象本身仍由 mPendingInputEventHead 持有。消息被合并或取消,不会改变 native InputConsumer 已经读取的 事件,必须看 ViewRoot 队列和 receiver ACK 状态。

9. 阶段链 ​

源码文件:frameworks/base/core/java/android/view/ViewRootImpl.java

doProcessInputEvents() 循环取出 ViewRoot 队列中的事件,并交给 deliverInputEvent();后者 根据 flags 选择 InputStage 链,最终由 ViewPostIme 阶段进入 View 树或由其它 stage 消费。

java
void doProcessInputEvents() {
    while (mPendingInputEventHead != null) {
        QueuedInputEvent q = mPendingInputEventHead;
        mPendingInputEventHead = q.mNext;
        if (mPendingInputEventHead == null) {
            mPendingInputEventTail = null;
        }
        q.mNext = null;
        mPendingInputEventCount -= 1;
        deliverInputEvent(q);
    }

    if (mProcessInputEventsScheduled) {
        mProcessInputEventsScheduled = false;
        mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
    }
}

队列排空后,事件才进入 InputStage 责任链:

java
private void deliverInputEvent(QueuedInputEvent q) {
    InputStage stage;
    if (q.shouldSendToSynthesizer()) {
        stage = mSyntheticInputStage;
    } else {
        boolean canSkipToPostIme = q.shouldSkipIme()
                && (q.mFlags & QueuedInputEvent.FLAG_SKIP_IME) == 0;
        stage = canSkipToPostIme ? mFirstPostImeInputStage : mFirstInputStage;
    }
    if (stage != null) {
        handleWindowFocusChanged();
        stage.deliver(q);
    } else {
        finishInputEvent(q);
    }
}

这里的 Handler 只负责把 ViewRoot 队列推进,InputStage 才是事件语义的消费者。不能把 MSG_PROCESS_INPUT_EVENTS 的处理当成“已经触发 onTouch”;它可能还要经过 IME、快捷键、 native 和合成事件阶段。

10. 确认回写 ​

源码文件:frameworks/base/core/java/android/view/ViewRootImpl.java

事件处理结束后,ViewRootImpl 调用 finishInputEvent(q);若事件来自 InputEventReceiver, 它根据 handled 和 compatibility 状态调用 receiver 的 finishInputEvent(),否则只回收 Java 事件。

java
private void finishInputEvent(QueuedInputEvent q) {
    if (q.mReceiver != null) {
        boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
        q.mReceiver.finishInputEvent(q.mEvent, handled);
    } else {
        q.mEvent.recycleIfNeededAfterDispatch();
    }
    recycleQueuedInputEvent(q);
}

Java 输入事件确认随后进入 receiver 的 sequence map 和 native ACK 路径:

源码文件:frameworks/base/core/java/android/view/InputEventReceiver.java

finishInputEvent() 从 mSeqMap 取出 dispatcher sequence,删除映射,再调用 native nativeFinishInputEvent()。重复确认、错误对象或 receiver 已 dispose 都只会记录警告,不能 产生有效 ACK。

java
public final void finishInputEvent(InputEvent event, boolean handled) {
    if (event == null) {
        throw new IllegalArgumentException("event must not be null");
    }
    if (mReceiverPtr == 0) {
        Log.w(TAG, "Attempted to finish an input event but receiver disposed.");
    } else {
        int index = mSeqMap.indexOfKey(event.getSequenceNumber());
        if (index < 0) {
            Log.w(TAG, "Attempted to finish an input event that is not in progress.");
        } else {
            int seq = mSeqMap.valueAt(index);
            mSeqMap.removeAt(index);
            nativeFinishInputEvent(mReceiverPtr, seq, handled);
        }
    }
    event.recycleIfNeededAfterDispatch();
}

ACK 的 owner 仍是 native receiver;Java Handler 只保证事件被消费到 finish 路径,不直接向 InputDispatcher 写 socket。

11. 批量输入 ​

源码文件:frameworks/base/core/java/android/view/ViewRootImpl.java

ViewRootImpl 可以把 batched input 消费安排到 Choreographer 的 CALLBACK_INPUT 阶段,也可 在 unbuffered 输入时立即消费。两条路径最终都调用 doConsumeBatchedInput(),再调用 doProcessInputEvents()。

java
void scheduleConsumeBatchedInput() {
    if (!mConsumeBatchedInputScheduled
            && !mConsumeBatchedInputImmediatelyScheduled) {
        mConsumeBatchedInputScheduled = true;
        mChoreographer.postVsyncCallback(
                Choreographer.CALLBACK_INPUT, mConsumedBatchedInputCallback);
    }
}

boolean doConsumeBatchedInput(long frameTimeNanos) {
    final boolean consumedBatches = mInputEventReceiver != null
            && mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos);
    doProcessInputEvents();
    return consumedBatches;
}

这说明“输入事件异步消息绕过屏障”和“批量输入在帧 INPUT 阶段消费”是两个互补机制:前者 保证即时消息可被 Looper 看到,后者利用 frame time 合并运动事件。

12. Receiver 清理 ​

源码文件:frameworks/base/core/jni/android_view_InputEventReceiver.cpp

dispose 先处理待发送的 outbound ACK,再移除 FD callback、销毁 InputConsumer,并从 native Looper 移除该 receiver 的 message。注释明确要求调用方在 dispose 前完成已经读出事件的 finishInputEvent(),否则可能留下未确认事件。

cpp
void NativeInputEventReceiver::dispose() {
    processOutboundEvents();
    setFdEvents(0);
    mInputConsumer.reset();
    mMessageQueue->getLooper()->removeMessages(this);
}

源码文件:frameworks/base/core/java/android/view/ViewRootImpl.java

窗口销毁时 ViewRootImpl dispose 自己的 mInputEventReceiver,使后续 channel 事件不再进入 ViewRoot;但已经进入 mPendingInputEvent 的 Java 对象仍要由 ViewRoot 清理路径负责回收。

java
if (mInputEventReceiver != null) {
    mInputEventReceiver.dispose();
    mInputEventReceiver = null;
}

清理不是“把 Handler 消息删掉”这么简单,而是同时关闭 FD、native consumer、Java sequence map 和 ViewRoot 输入队列的所有权。

13. 失败分支 ​

源码文件:frameworks/base/core/jni/android_view_InputEventReceiver.cpp

输入 channel 出现 HANGUP/ERROR 时,native Looper callback 返回 REMOVE_CALLBACK;消费返回 不可恢复错误时也会移除 FD 监听。Java 异常由 MessageQueue 的 JNI 桥接抛回并清理,不会假设 事件已经成功进入 View 树。

cpp
if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
    return REMOVE_CALLBACK;
}

status_t status = consumeEvents(env, false, -1, nullptr);
mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK;

失败分支的证明范围是“receiver 不再继续消费”,不是“InputDispatcher 已经收到失败 ACK”。 是否产生未处理、超时或窗口移除结果,还要回到 channel 对端和系统输入服务状态。

14. 验证输入 ​

源码文件:frameworks/base/core/java/android/view/InputEventReceiver.java

第一组验证构造契约:传入 null InputChannel 或 null Looper,断言构造前抛出 IllegalArgumentException;它不证明 native FD 已注册。

java
assertThrows(IllegalArgumentException.class,
        () -> new InputEventReceiver(null, Looper.getMainLooper()));
assertThrows(IllegalArgumentException.class,
        () -> new InputEventReceiver(inputChannel, null));

第二组验证序号/确认:native dispatch 将 event sequence 写入 mSeqMap,随后调用 finishInputEvent();应断言该 sequence 被移除,重复 finish 只能得到警告,不会再次发送同一 确认。这个测试需要 native peer 或受控 fake,不应伪装成纯 Java 单元测试。

源码文件:frameworks/base/core/java/android/view/ViewRootImpl.java

第三组验证 ViewRoot 队列:注入两个事件,确认 mPendingInputEventCount 从 0 变为 2, doProcessInputEvents() 按接收顺序消费,并在处理结束后清除 MSG_PROCESS_INPUT_EVENTS。 这证明 Java 队列和 Handler 去重,不证明 InputDispatcher 的窗口选择。

15. 复查命令 ​

源码文件:

  • frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
  • frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
  • frameworks/native/services/inputflinger/InputManager.cpp
  • frameworks/base/core/java/android/view/InputEventReceiver.java
  • frameworks/base/core/jni/android_view_InputEventReceiver.cpp
  • frameworks/base/core/java/android/view/ViewRootImpl.java
bash
rg -n "public void start|mNative.start|nativeInit|nativeStart" \
  frameworks/base/services/core/java/com/android/server/input/InputManagerService.java \
  frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

rg -n "class NativeInputEventReceiver|initialize|setFdEvents|handleEvent|consumeEvents|dispose" \
  frameworks/base/core/jni/android_view_InputEventReceiver.cpp

rg -n "dispatchInputEvent|finishInputEvent|nativeFinishInputEvent|mSeqMap" \
  frameworks/base/core/java/android/view/InputEventReceiver.java

rg -n "enqueueInputEvent|scheduleProcessInputEvents|MSG_PROCESS_INPUT_EVENTS|doProcessInputEvents|finishInputEvent" \
  frameworks/base/core/java/android/view/ViewRootImpl.java

复述主线时,应明确四个边界:InputDispatcher 写入 channel,native Looper 触发 receiver, ViewRootHandler 推进 Java 输入队列,InputEventReceiver 发送 ACK。任意一步缺失,都不能把 “事件已到达 View”作为结论。

16. 适用边界 ​

这条路径适合解释已有窗口 InputChannel 的输入事件如何跨 native/Java 边界进入 ViewRoot。 它不等价于 InputManagerService 的全部职责,也不覆盖输入设备读取、焦点窗口选择、触摸命中、 ANR 定时器或完整 InputStage 实现。

排查输入延迟时,按源码顺序检查:FD 是否仍注册,native consumer 是否成功 decode,Java sequence 是否写入,ViewRoot 是否因 processImmediately 立即消费或排队,异步消息是否被 Looper 取出,InputStage 是否完成,以及 finishInputEvent() 是否回写 ACK。这样才能区分 channel 无事件、native 消费失败、主线程繁忙、View 阶段未处理和确认丢失。