当前位置: 首页 > news >正文

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、处理滑动冲突、还是优化触摸响应延迟,都能从源码层面找到根本原因。

http://www.jsqmd.com/news/1098960/

相关文章:

  • 英雄联盟国服免费换肤终极指南:R3nzSkin完全教程
  • 抖音内容保存终极指南:douyin-downloader让你的收藏变得轻松高效
  • 英伟达“技术没有秘密“合理吗:研发总监拆解护城河的真相
  • 多 Agent 路由设计:当不同渠道、不同用户需要匹配不同“大脑”
  • 智能零售结账系统 文具用品识别数据集 YOLO与OpenCV实现+文具店橡皮+铅笔+尺子识别
  • 链表相关的算法
  • 北京昆仑数智-sql学习笔记
  • 爬虫去重别只会用Set!Python实现亿级数据清洗的4种工业级方案
  • 【VMware OVF导出终极指南】:20年资深架构师亲授5大避坑要点与3种加速导出实战技巧
  • 【数字孪生国标落地第一个月,我给新能源行业测了测段位】
  • 主流开源LLM(Qwen、ChatGLM等)的本地化部署
  • 验厂时,食品工作服需要注意什么?
  • GoalFlow:四、轨迹评分筛选模块(Trajectory Scorer, M3)
  • ps怎么调整图片大小?ps调整图片大小快捷键
  • 虚拟摇杆vJoy:Windows游戏控制器模拟的技术深度解析
  • 查新报告分为哪几种?科技查新、查收查引与专利查新区别
  • 基于 VC++ 与机器人 SDK 的工业多轴示教器软件设计与实现
  • 驾驶行为识别 打电话识别数据集 驾驶注意力监控 驾驶分心识别数据集 危险驾驶行为检测 抽烟打电话 睡觉 吃东西识别图像数据集第10149期
  • Metasploit渗透测试实战:从漏洞利用到后渗透操作详解
  • 车的使用年限,从来不是出厂定的!
  • OpenClaw排坑实录:启动失败、技能失效、模型报错,30个高频问题一次讲透
  • 解决方案|腾讯安全天御金融反电诈产品解决方案
  • 【LeetCode Hot100】189.轮转数组-三种解法以及效果评估
  • 搞定99%安装问题!OpenClaw 完整部署与故障修复
  • G-Helper终极指南:重新定义华硕笔记本硬件控制的轻量级革命
  • 模块 包 循环导入 系统
  • 3PEAK思瑞浦 TPA133A2-VS1R-S MSOP8 电流信号检测放大器
  • AI4C编译调优的终极技巧:提升程序性能30%的秘密
  • NCM音乐格式转换终极指南:5分钟解锁你的音乐收藏
  • Mineradio开源音乐播放器下载安装介绍(附下载链接)