三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

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

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 向 socketsend()数据时,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 共享同一事件循环
NativeInputEventReceiverJNI 层桥接,读 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、处理滑动冲突、还是优化触摸响应延迟,都能从源码层面找到根本原因。

← 返回列表