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 桥接层本文中所有 Java 代码块均来自上述文件路径,不再逐一标注。
二、InputChannel 的创建
2.1 ViewRootImpl.setView() 触发通道创建
源码路径:frameworks/base/core/java/android/view/ViewRootImpl.java
publicvoidsetView(Viewview,...){mView=view;// WMS 在 addWindow 时调用 openInputChannelPair()// server 端 → InputDispatcher,client 端 → 通过 Binder 回传res=mWindowSession.addToDisplay(mWindow,...);if(mInputChannel!=null){mInputEventReceiver=newWindowInputEventReceiver(mInputChannel,Looper.myLooper());}}2.2 WMS 侧创建 socket pair
// WindowManagerService.javaInputChannel[]inputChannels=InputChannel.openInputChannelPair(name);// server 端 → 注册到 InputDispatchermInputManager.registerInputChannel(inputChannels[0],...);// client 端 → 通过 Binder 回传给 ViewRootImploutInputChannel=inputChannels[1];三、NativeInputEventReceiver:socket fd 与 MessageQueue 集成
3.1 JNI 初始化
// android_view_InputEventReceiver.cppstaticjlongnativeInit(JNIEnv*env,jclass clazz,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=newNativeInputEventReceiver(env,receiverWeak,inputChannel,messageQueue);receiver->initialize();returnreinterpret_cast<jlong>(receiver.get());}3.2 核心:将 socket fd 注册到 MessageQueue 的 Looper
voidNativeInputEventReceiver::setFdEvents(intevents){intfd=mInputConsumer.getChannel()->getFd();// socket 的 fd// 将 socket fd 加入 MessageQueue 的 Native Looper 的 epoll 实例mMessageQueue->getLooper()->addFd(fd,0,// identALOOPER_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 层
intNativeInputEventReceiver::handleEvent(intreceiveFd,intevents,void*data){if(events&(ALOOPER_EVENT_ERROR|ALOOPER_EVENT_HANGUP)){return0;// 移除回调}if(events&ALOOPER_EVENT_INPUT){JNIEnv*env=AndroidRuntime::getJNIEnv();consumeEvents(env,false,-1,NULL);}return1;}status_tNativeInputEventReceiver::consumeEvents(JNIEnv*env,boolconsumeBatches,nsecs_t frameTime,bool*outConsumedBatch){for(;;){uint32_tseq;InputEvent*inputEvent;status_t status=mInputConsumer.consume(&mInputEventFactory,consumeBatches,frameTime,&seq,&inputEvent);if(status==OK){// 回调 Java 层的 dispatchInputEvent()env->CallVoidMethod(receiverObj.get(),gInputEventReceiverClassInfo.dispatchInputEvent,seq,inputEventObj);}elseif(status==WOULD_BLOCK){break;// 没有更多事件}}}3.4 事件消费回复(Finished Signal)
status_tNativeInputEventReceiver::sendFinishedSignal(uint32_tseq,boolhandled){// 通过同一 socket 发送 Finished 消息给 InputDispatcherreturnmInputConsumer.sendFinishedSignal(seq,handled);}APP 必须调用sendFinishedSignal来告知 InputDispatcher 事件已被消费。如果长时间不调用,就会触发 ANR 超时。
四、InputEventReceiver(Java 层基类)
publicabstractclassInputEventReceiver{privatelongmReceiverPtr;// NativeInputEventReceiver 指针publicInputEventReceiver(InputChannelinputChannel,Looperlooper){mInputChannel=inputChannel;mReceiverPtr=nativeInit(newWeakReference<>(this),inputChannel,looper.getQueue());}// JNI 回调此方法privatevoiddispatchInputEvent(intseq,InputEventevent){onInputEvent(event);}publicabstractvoidonInputEvent(InputEventevent);// 通知 Dispatcher 事件已消费publicfinalvoidfinishInputEvent(InputEventevent,booleanhandled){nativeFinishInputEvent(mReceiverPtr,seq,handled);}}五、ViewRootImpl:窗口事件中枢
5.1 WindowInputEventReceiver
finalclassWindowInputEventReceiverextendsInputEventReceiver{@OverridepublicvoidonInputEvent(InputEventevent){enqueueInputEvent(event,this,0,true);}}5.2 事件入队与处理
voidenqueueInputEvent(InputEventevent,InputEventReceiverreceiver,intflags,booleanprocessImmediately){QueuedInputEventq=obtainQueuedInputEvent(event,receiver,flags);// 加入队列尾if(mPendingInputEventTail==null){mPendingInputEventHead=q;}else{mPendingInputEventTail.mNext=q;}mPendingInputEventTail=q;if(processImmediately){doProcessInputEvents();}else{scheduleProcessInputEvents();// 调度到下一帧}}voiddoProcessInputEvents(){while(mPendingInputEventHead!=null){QueuedInputEventq=mPendingInputEventHead;mPendingInputEventHead=q.mNext;deliverInputEvent(q);}}5.3 InputStage 责任链流水线
ViewRootImpl 用责任链模式处理输入事件,依次经过 7 个 Stage:
// Stage 链顺序(从第一个到最后一个):abstractclassInputStage{// 1. NativePreImeInputStage → 原生输入法之前处理// 2. ViewPreImeInputStage → View 分发输入法相关事件// 3. ImeInputStage → 输入法(IME)处理// 4. EarlyPostImeInputStage → 输入法之后、View 之前// 5. NativePostImeInputStage → 原生 View 后处理// 6. ViewPostImeInputStage → View 分发普通事件(核心!)// 7. SyntheticInputStage → 合成事件(导航键等)}最核心的ViewPostImeInputStage:
finalclassViewPostImeInputStageextendsInputStage{@OverrideprotectedintonProcess(QueuedInputEventq){if(q.mEventinstanceofKeyEvent){returnprocessKeyEvent(q);}else{returnprocessPointerEvent(q);}}privateintprocessPointerEvent(QueuedInputEventq){MotionEventevent=(MotionEvent)q.mEvent;booleanhandled=mView.dispatchPointerEvent(event);// → DecorView.dispatchTouchEvent(event)returnhandled?FINISH_HANDLED:FORWARD;}privateintprocessKeyEvent(QueuedInputEventq){KeyEventevent=(KeyEvent)q.mEvent;booleanhandled=mView.dispatchKeyEvent(event);returnhandled?FINISH_HANDLED:FORWARD;}}六、View 树的事件分发
6.1 触摸事件分发(ViewGroup)
// ViewGroup.java@OverridepublicbooleandispatchTouchEvent(MotionEventev){booleanhandled=false;// 1. 检查是否被拦截finalbooleanintercepted;if(actionMasked==MotionEvent.ACTION_DOWN||mFirstTouchTarget!=null){intercepted=onInterceptTouchEvent(ev);}else{intercepted=true;}// 2. 不拦截则分发给子 Viewif(!intercepted){finalView[]children=mChildren;for(inti=childrenCount-1;i>=0;i--){// 按 Z 序从上层到下层Viewchild=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()}returnhandled;}6.2 View 的事件处理
// View.javapublicbooleandispatchTouchEvent(MotionEventevent){// 1. 先回调 OnTouchListenerif(mOnTouchListener!=null&&mOnTouchListener.onTouch(this,event)){returntrue;}// 2. 再回调 onTouchEventif(onTouchEvent(event)){returntrue;}returnfalse;}publicbooleanonTouchEvent(MotionEventevent){if((viewFlags&CLICKABLE)==CLICKABLE){switch(event.getAction()){caseMotionEvent.ACTION_UP:performClick();// 执行点击break;caseMotionEvent.ACTION_DOWN:checkForLongClick(0);// 延迟长按检测break;}returntrue;}returnfalse;}6.3 按键事件分发
按键事件优先交给焦点 View:
// ViewGroup.javapublicbooleandispatchKeyEvent(KeyEventevent){if(mFocused!=null){returnmFocused.dispatchKeyEvent(event);}returnsuper.dispatchKeyEvent(event);}// View.javapublicbooleandispatchKeyEvent(KeyEventevent){// 1. 回调 OnKeyListenerif(mOnKeyListener!=null&&mOnKeyListener.onKey(this,event.getKeyCode(),event)){returntrue;}// 2. 回调 onKeyDown / onKeyUpreturnevent.dispatch(this,...);}七、消费信号回传闭环
// ViewRootImpl.javaprivatevoidfinishInputEvent(QueuedInputEventq){if(q.mReceiver!=null){booleanhandled=(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、处理滑动冲突、还是优化触摸响应延迟,都能从源码层面找到根本原因。
