广告

Android键盘事件全流程解析

2024-07-08 阅读:  
在日常开发中,我们经常需要跟电视、机顶盒等非触屏设备打交道。而这些设备基本都是采用遥控器来交互的,即属于Android中的KeyEvent键盘事件。因此对整个键盘事件的处理流程有清晰认识是非常重要的。

导读

在日常开发中,我们经常需要跟电视、机顶盒等非触屏设备打交道。而这些设备基本都是采用遥控器来交互的,即属于Android中的KeyEvent键盘事件。因此对整个键盘事件的处理流程有清晰认识是非常重要的。它有助于我们开发出交互合理、体验优良的产品。而一直以来,市面上缺乏对整个事件流程的完整介绍,绝大多数都是从事件到达View树层开始介绍的。于是,本文尝试对这一主题进行深入浅出的介绍,希望能填补这方面的空白,也希望提升大家对键盘事件的整体认知。

第一步之硬件输入

本文中,我们希望从最底层说起,即硬件输入层。当用户按下或释放物理按键时,相应的硬件(如遥控器、键盘)会生成一个硬件中断。中断是一种信号,用来通知CPU有一个需要立即处理的事件。一旦中断信号被生成,CPU中的中断控制器将捕捉到这个中断信号,并将其传递给操作系统内核。系统内核中有一个中断处理程序(Interrupt Handler),专门用于处理各种硬件中断。对于键盘中断,内核会调用相应的键盘驱动程序来处理中断(比如获取具体哪个键被按下还是释放)。

第二步之系统内核

Android基于Linux内核,其输入子系统接收到硬件中断后,会处理并转换这个中断信号为内核层面的输入事件。在内核的输入子系统中,事件被进一步处理和规范化,接着通过/dev/input设备节点将处理好的键盘事件传递到用户空间的Android输入系统,准备开始下一阶段的处理。

第三步之Android输入系统

Android输入系统由InputManagerService (IMS) 管理,并运行在system_server进程中。IMS 在启动时会创建一个InputReader线程,负责不断地从/dev/input设备节点读取原始输入事件数据,并将其封装成KeyEvent对象。KeyEvent对象包含了事件类型(按下、抬起、长按等)、键码、时间戳等信息。

InputReader处理完事件后,将其传递给InputDispatcher。InputDispatcher 是另一个IMS创建的线程,其主要任务是将输入事件分发给合适的窗口(Window)。

第四步之WMS

Window Manager Service,下文简称WMS,负责管理整个系统的应用窗口,小到Toast提醒,大到Activity这种全屏的页面,都在其管辖范围内。WMS决定由哪个窗口来接收键盘事件。通常,它会将事件交给当前拥有焦点的窗口。而我们知道这个事件,最终会来到App进程的UI线程里。那这过程中具体又经历了什么,它又是怎么就来到了另一个进程中的呢?

这里就要提到另一个关键的组件了,即InputChannel。InputChannel是 Android 用来在InputDispatcher和应用窗口之间传递输入事件的机制。它实际上是一个内存映射的文件描述符对,用于进程间通信 (IPC)。InputChannel的主要作用是提供一个高效的途径,将输入事件从system_server进程传递到应用进程中的主线程。

每个窗口都有一个 InputChannel,这是窗口与InputDispatcher之间通信的通道。InputChannel由ViewRootImpl管理,而ViewRootImpl又是连接窗口和视图层次结构的桥梁。它的具体工作原理如下:

  • 创建和分配:当一个窗口被创建时,系统会为它分配一个InputChannel对象。这个InputChannel对象由两个端点组成,一个在system_server进程中,另一个在应用进程中。每个端点实际是一个Binder对象
  • System Server端:在system_server进程中,InputDispatcher使用它的 InputChannel端点来发送输入事件。System Server通过InputManagerService将另一个端点传递给应用进程,应用进程则持有这个端点以接收输入事件。
  • 应用进程端:在应用进程中,窗口的ViewRootImpl持有InputChannel的另一端,负责接收来自InputDispatcher的事件。
  • 事件传递:当InputDispatcher准备好要分发一个输入事件时,它将事件写入它的InputChannel端点。事件通过Binder机制传递到应用进程中的 InputChannel端点。
  • 接收和处理:应用进程中的ViewRootImpl监听InputChannel端点并从中读取事件。ViewRootImpl将这些事件放入应用程序的消息队列中,然后在UI线程中进行处理和分发。

InputChannel使用Binder IPC机制实现了在 system_server进程和应用程序进程之间的高效通信。Binder作为Android核心的IPC机制,提供了可靠且高效的进程间通信支持,使得输入事件能够快速且安全地在不同进程之间传递。

第五步之View层次结构

经历了前面的一系列流程,现在键盘事件终于是来到了目标App的UI线程里了,更具体的是到了当前窗口的ViewRootImpl。详细的Java堆栈如图1所示:

图1 KeyEvent调用堆栈

从图上,可以看出事件是先到了ViewRootImpl,然后是DecorView,再是Activity#dispatchKeyEvent,最后是沿着View树的层次结构继续分发。View树层次结构中,不论哪个View对事件进行了消费处理,此事件的派发就此结束,否则当整个View树都没有处理事件时,事件最终又会回到Activity里,其onKeyDown、onKeyUp之类的回调方法会被触发。

总结

Android的键盘事件处理流程从硬件输入开始,经过内核、Android输入系统、窗口管理器,最终到达应用进程的活动和视图层次结构。在应用层,各种 dispatchKeyEvent和onKeyDown、onKeyUp方法提供了开发者处理键盘事件的机会。通过深入理解这个完整的处理流程,开发者可以更好地控制和响应用户输入,为开发体验优良的应用打好扎实基础。

本文为EET电子工程专辑 原创文章,禁止转载。请尊重知识产权,违者本司保留追究责任的权利。
您可能感兴趣的文章
相关推荐
    广告
    近期热点
    广告
    广告
    可能感兴趣的话题
    广告
    广告
    向右滑动:上一篇 向左滑动:下一篇 我知道了