Android 按键消息流程源码分析

news2024/12/23 11:21:49

在Android系统中,键盘按键事件是由SystemServer服务来管理的;然后在以消息的形式分发给应用程序处理。产生键盘按键事件则是有Linux kernel的相关驱动来实现。键盘消息有别于其他类型的消息;需要从Linux kernel drivers产生由上层APP来处理。同时按键有着不同的映射值,因此从模块独立性角度各个独立的模块应该拥有不同的键盘映射。这样以来,kernel产生的按键事件必然回经过不同的映射才到APP。
Android使用标准的Linux输入事件设备(/dev/input/)和驱动按键定义在Linux内核include/linux/input.h中,按键的定义形式如下(仅以BACK HOME MENU为例):
在这里插入图片描述

Kernel内核驱动会产生事件,在这里就不讨论了,不是本文的范畴;下面分析Framework事件处理流程。

SystemServer.java
在这里插入图片描述

可以看到,在系统启动的时候,会首先创建一个系统级别的Handler线程wmHandlerThread用于处理键盘消息(仅说明键盘消息)。然后在创建输入管理服务inputManager,InputManagerService的第二个参数就是用于处理按键消息的Handler。

InputManagerService.java
进入InputManagerService的构造函数:
在这里插入图片描述

这里做了重要的两件事情,第一:将SystemServer级别的Handler赋值给InputManagerService自己的消息处理Handler;第二:调用nativeInit继续进行初始化。

com_android_server_input_InputManagerService.java
那就看看本地初始化:
在这里插入图片描述

进入NativeInputManager构造函数:
在这里插入图片描述
这里需要特别注意最后两行代码。第一:创建了EventHub;第二:创建InputManager并将EventHub作为参数传入InputManager。

InputManager.cpp
接下来继续看看InputManager的构造函数:
在这里插入图片描述
创建了InputDispatcher和InputReader,请注意00032行,mDispatcher作为了InputReader参数,你看看InputReader的构造函数就知道为什么要这么做了;后面调用了initialize函数创建了InputReaderThread和InputDispatcherThread。InputDispatcher类是负责把键盘消息分发给当前激活的Activity窗口的,而InputReader类则是通过EventHub类来实现读取键盘事件的,InputReader实列mReader就是通过这里的InputReaderThread线程实列mReaderThread来读取键盘事件的,而InputDispatcher实例mDispatcher则是通过这里的InputDispatcherThread线程实例mDisptacherThread来分发键盘消息的;到这里,相关的组件都已经被创建了。
在systemServer.java中创建inputManager之后。将InputManagerServer进行注册,并运行start()(在第一页有相关代码)。

com_android_server_input_InputManagerService.java
会来到这里:
在这里插入图片描述

继续往下则会调用到InputManager.cpp的start()函数:
在这里插入图片描述
这个函数主要就是分别启动一个InputDispatcherThread线程和一个InputReaderThread线程来读取和分发键盘消息的了。这里的InputDispatcherThread线程对象mDispatcherThread和InputReaderThread线程对象mReaderThread是在前面的第三页中创建的,调用了它们的run函数后,就会进入到它们的threadLoop函数中去,只要threadLoop函数返回true,函数threadLoop就会一直被循环调用,于是这两个线程就起到了不断地读取和分发键盘消息的作用

在这里插入图片描述

继续看loopOnce()这个函数:
在这里插入图片描述
这里面需要注意像神一样的函数mEventHub->getEvents()。其实现原理,还有点不是很清楚;但是其功能就是负责键盘消息的读取工作,如果当前有键盘事件发生或者有键盘事件等待处理,通过mEventHub的**getEvent(这个函数里面很有讲究,有空自己分析,这里不做深入的讲解)**函数就可以得到这个事件,然后交给processEventsLocked函数进行处理。同样需要特别注意最后一行(太长了,没有截过来);后面回解释。我们还会回来的…不过还想补充一点的就是,读取的数据的路径为:
在这里插入图片描述

机型为OK1000:
在这里插入图片描述

通过getEvents获取事件消息后,就得开始处理消息了,回到第六页00314行:
在这里插入图片描述

如果是一般的消息就调用*processEventsForDeviceLocked()*函数;否者去处理设备的ADDED、REMOVED、SCAN事件;那我们就先分析设备的ADD吧,因为后面的处理是在这个基础上的,回到上一页00372行:
在这里插入图片描述

在行00400create一个InputDevice:
在这里插入图片描述

所有类型的设备都在这里生产,然后统一添加到00412行的mDivices;提供给后续具体设备处理,下面就是具体设备的处理过程。
在第七页*processEventsForDeviceLocked()*函数,根据deviceId来处理相应的事件消息:
在这里插入图片描述
我就在想:问什么不直接到process函数呢?其实我觉得这里体现了设计模式中的单一职责原则(多态);这种设计可以有效的控制函数粒度(有个类粒度,这里自创函数粒度)的大小,函数承担的职责越多其复用的可能性就越小,并且当期中某一个职责发生变化,可能会影响其他职责的运作!继续往下走吧…
在这里插入图片描述

这里的每一个事件都要做一个循环处理,代码在01022行,至于为什么你看(行00993)注释就会明白了。
在这里插入图片描述

行02107函数processKey的原型中,有部分代码片段如下:
在这里插入图片描述
看到关键行02216了吧!再回头看看第三页就知道跳转到哪里去了。

总结:
在这里插入图片描述

一不小心来到了这里…
InputDispatcher.cpp
在函数notifyKey中有这样的代码片段:

在这里插入图片描述

行02421函数的功能其实很简单,主要是把EventEntry添加到一个待发的事件队列当中,源代码如下:
在这里插入图片描述

在看看行02424,功能就是唤醒InputDispatcherThread线程,然后就开始执行InputDispatcher的threadLoop函数,之后就调用InputDispatcher的dispatchOnce方法,代码如下:
在这里插入图片描述

进入行00230,其中有这样代码片段:
在这里插入图片描述

进入行00365,其中有这样代码片段:
在这里插入图片描述

行00780,查找焦点窗口,然后进入行00794
dispatchEventLocked()->
prepareDispatchCycleLocked()->
enqueueDispatchEntriesLocked()->
startDispatchCycleLocked()->

其中函数startDispatchCycleLocked()有这样代码片段:
在这里插入图片描述

看到publishKeyEvent()了吧!

InputTransprot.cpp
在这里插入图片描述

继续走:
在这里插入图片描述
总算send出去了。

总结:
在这里插入图片描述

至于外面怎么接收到消息的,后续再分析,内容太多了,自己还没有完全搞懂,等搞懂了再补上…

参考文献:
http://blog.sina.com.cn/s/blog_6268defa0101ad1o.html
http://www.cnblogs.com/haiming/p/3318614.html
http://blog.chinaunix.net/uid-27167114-id-3347185.html
http://my.oschina.net/u/994235/blog/294227
http://blog.csdn.net/zjq2008wd/article/details/39225539
http://blog.csdn.net/powq2009/article/details/8426271
http://blog.sina.com.cn/s/blog_89f592f50101394l.html
http://www.cnblogs.com/lcw/p/3374466.html
http://blog.sina.com.cn/s/blog_89f592f50101394l.html
http://blog.csdn.net/zjq2008wd/article/category/1283349

觉得本文对您有用,麻烦点赞、关注、收藏,您的肯定是我创作的无限动力,谢谢!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1655583.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

正点原子Linux学习笔记(六)在 LCD 上显示 jpeg 图像

在 LCD 上显示 jpeg 图像 20.1 JPEG 简介20.2 libjpeg 简介20.3 libjpeg 移植下载源码包编译源码安装目录下的文件夹介绍移植到开发板 20.4 libjpeg 使用说明错误处理创建解码对象设置数据源读取 jpeg 文件的头信息设置解码处理参数开始解码读取数据结束解码释放/销毁解码对象 …

[华为OD]C卷 BFS 亲子游戏 200

题目: 宝宝和妈妈参加亲子游戏,在一个二维矩阵(N*N)的格子地图上,宝宝和妈妈抽签决定各自 的位置,地图上每个格子有不同的Q糖果数量,部分格子有障碍物。 游戏规则Q是妈妈必须在最短的时间&a…

力扣刷题第1天:消失的数字

大家好啊,从今天开始将会和大家一起刷题,从今天开始小生也会开辟新的专栏。😜😜😜 目录 第一部分:题目描述 第二部分:题目分析 第三部分:解决方法 3.1 思路一:先排序…

WebView基础知识以及Androidx-WebKit的使用

文章目录 摘要WebView基础一、启动调整模式二、WebChromeClient三、WebViewClient四、WebSettings五、WebView和Native交互 Androidx-WebKit一、启动安全浏览服务二、设置代理三、安全的 WebView 和 Native 通信支持四、文件传递五、深色主题的支持六、JavaScript and WebAssem…

邮件大附件系统如何进行安全、高效的大附件发送?

邮件大附件系统是一套解决传统电子邮件系统,在发送大文件时遇到限制的解决方案。由于传统电子邮件系统通常对附件大小有限制,这使得发送大文件变得困难。邮件大附件系统通过各种技术手段,允许用户发送超过传统限制的大文件,通常在…

【随笔】Git 高级篇 -- 上传命令的参数 (下)git push(三十七)

💌 所属专栏:【Git】 😀 作  者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! 💖 欢迎大…

目标检测——低光可见光-红外配对数据集

引言 亲爱的读者们,您是否在寻找某个特定的数据集,用于研究或项目实践?欢迎您在评论区留言,或者通过公众号私信告诉我,您想要的数据集的类型主题。小编会竭尽全力为您寻找,并在找到后第一时间与您分享。 …

TypeScript学习日志-第二十四天(webpack构建ts+vue3)

webpack构建tsvue3 一、构建项目目录 如图: shim.d.ts 这个文件用于让ts识别.vue后缀的 后续会说 并且给 tsconfig.json 增加配置项 "include": ["src/**/*"] 二、基础构建 安装依赖 安装如下依赖: npm install webpack -D …

11.偏向锁原理及其实战

文章目录 偏向锁原理及其实战1.偏向锁原理2.偏向锁案例代码演示2.1.偏向锁案例代码2.2.1.无锁情况下状态2.1.2.偏向锁状态2.1.3.释放锁后的状态 2.2.偏向锁的膨胀和撤销2.2.1.偏向锁撤销的条件2.2.2.偏向锁的撤销 2.2.3.偏向锁的膨胀 2.3.全局安全点原理和偏向锁撤销性能问题2.…

在R的 RGui中,使用devtools 安装trajeR

创建于:2024.5.5 文章目录 1. 报错信息2. 尝试使用指定的清华镜像,没有解决3. 找到原因:官网把包删除了4. 尝试从网上下载,然后安装。没有成功5. 使用devtools安装5.1 尝试直接安装:install.packages("devtools&q…

OpenCV | 项目 | 虚拟绘画

OpenCV | 项目 | 虚拟绘画 捕捉摄像头 如果在虚拟机中运行&#xff0c;请确保虚拟机摄像头打开。 #include<opencv2/opencv.hpp>using namespace cv; using namespace std;int main() {VideoCapture cap(0);Mat img;while(1) {cap.read(img);imshow("Image"…

JetBrains的Java集成开发环境IntelliJ 2024.1版本在Windows/Linux系统的下载与安装配置

目录 前言一、IntelliJ在Windows安装二、IntelliJ在Linux安装三、Windows下使用配置四、Linux下使用配置总结 前言 ​ “ IntelliJ IDEA Ultimate是一款功能强大的Java集成开发环境&#xff08;IDE&#xff09;。它提供了丰富的功能和工具&#xff0c;可以帮助开发人员更高效地…

labview技术交流-将时间字符串转换成时间格式

应用场景 我们在数据库中设计了datetime类型的字段&#xff0c;比如字段名就叫“保存时间”&#xff0c;当我们使用labview将表中数据读取出来后datetime类型的数据是以字符串的格式显示的。而我们想计算两条数据“保存时间”的间隔时间时&#xff0c;用字符串类型自然是没法计…

uniapp读取项目本地文件/json文件/txt文件

uniapp读取项目本地文件/json文件/txt文件 文件必须放在static目录下 方法&#xff1a; /*** 访问static里面的文件* param url 文件路径 必须在static目录下*/ function localFetch(url) {return new Promise((resolve, reject) > {plus.io.resolveLocalFileSystemURL(_ww…

OmniReader Pro mac激活版:智慧阅读新选择,开启高效学习之旅

在追求知识的道路上&#xff0c;一款优秀的阅读工具是不可或缺的。OmniReader Pro作为智慧阅读的新选择&#xff0c;以其独特的功能和卓越的性能&#xff0c;为您开启高效学习之旅。 OmniReader Pro具备高效的文本识别和处理技术&#xff0c;能够快速准确地提取文档中的关键信息…

PXE批量部署,一键安装配置多台Linux系统

目录 一、PXE批量部署的优点 二、搭建PXE远程安装服务器 1. 实验初始化设置 2. 一键安装软件包 3. 复制 vmlinuz、initrd.img、pxelinux.0文件 4. 配置PE启动菜单配置文件 5. 修改配置文件&#xff0c; 启动各个软件服务 6. kickstart自动应答文件修改启动菜单配置文件…

(一)Linux的vim编辑器的使用

一.vim编辑器 Vim 是从 vi 发展出来的一个文本编辑器。代码补全、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用。简单的来说, vi 是老式的字处理器,不过功能已经很齐全了,但是还是有可以进步的地方。 vim 则可以说是程序开发者的一项很好用的工具。 二…

不抽象:Increase API 设计原则

原文&#xff1a;Increase - 2024.04.26 &#xff08;注&#xff1a;Increase 是一家提供金融技术服务的公司。&#xff09; API 资源是 API 的实体或对象。决定如何为这些实体命名和建模可以说是设计 API 最难也是最重要的部分。您所公开的资源组织了用户对您的产品如何工作…

什么才是正确的领域驱动实现架构?

作为一种系统建模方法&#xff0c;DDD同样涉及系统的体系架构设计。区别于分布式、事件驱动、消息总线等架构设计方法&#xff0c;DDD中的架构设计关注前面各章所介绍的聚合、实体、值对象、领域事件、应用服务以及资源库之间的交互方式和风格&#xff0c;并在设计思想上有其独…

揭秘设计师必备神器:情绪板是什么?

每个伟大的设计项目都从一点灵感开始。无论你是在设计网站、应用程序&#xff0c;还是想重新装修房子&#xff0c;情绪板都可以帮助你激发创造力&#xff0c;甚至情绪板也可以决定UI界面是否成功。本文将分享什么是情绪板&#xff0c;为什么需要情绪板&#xff0c;以及如何充分…