Android 7系统输入(五):应用侧 — InputChannel、ViewRootImpl与事件消费

系列目录 :第一篇:从硬件到应用的事件旅程 | 第二篇: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 输入系统从硬件到应用的完整链路:

  1. 总览:建立六层架构的宏观认知
  2. EventHub:inotify + epoll 的设备监听与原始事件采集
  3. InputReader:InputMapper 体系与事件加工转换
  4. InputDispatcher:窗口定位、socket 通信与 ANR 超时机制
  5. 应用侧:InputChannel 到 View.onTouchEvent 的完整消费链路

每篇文章都是独立的知识模块,可以按需查阅。理解这套机制后,无论是排查 ANR、处理滑动冲突、还是优化触摸响应延迟,都能从源码层面找到根本原因。