IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android 事件处理系统 -> 正文阅读

[移动开发]Android 事件处理系统

https://www.cnblogs.com/lcw/p/3373214.html

1. Android 事件传递

Android 事件传递的流程,按键,触屏等事件
由WindowManagerServcie 获取,并通过共享内存和管道的方式传递给ViewRoot,ViewRoot再dispatch 给Application 的View。当有事件从硬件设备输入时,system_server端在检测到事件发生时,通过管道(pipe)通知ViewRoot事件发生,此时ViewRoot 再去内存中读取这个事件信息。

为什么使用共享内存而不是直接使用Binder机制?

Google 为了保证事件响应的实时性,在选择进程间传递事件的方式中,选择了共享内存的方式。
由于共享内存在数据管理过程中基本不涉及到内存的数据拷贝,只是在进程读写时涉及到2次数据拷贝,这个是不可避免的数据拷贝,因此这种方式能够很好的保证系统对事件的响应,但是仅仅是共享内存是不够的,因为共享内存的通信方式并不能够通知对方有数据更新,因此android 在事件处理过程中加入了另一种进程间通信方式 管道(pipe)。

管道的效率不如共享内存高,会不会影响事件处理的实时性?

没关系,每次system_serve 通知ViewRoot 只是向其传递一个字符,既轻巧又简单。

事件处理系统的初始化过程

Android 事件传递系统是以共享内存和管道的进程间通信方式来实现传递的。

创建管道连接

 管道的主要作用是在有事件被存储到共享内存中时,system_server 端通知ViewRoot去读取事件的通信机制。

ViewRoot 和 WindowManagerService (负责事件传递,运行在system_server进程中) 各需维护管道的一个文件描述符。实现全双工的管道通信。
WindowManagerService —> ViewRoot 方向的管道通信,表示WMS通知ViewRoot 有新事件被写入到共享内存。
ViewRoot --> WindowManagerService 方向的管道通信,表示ViewRoot 已经消化完共享内存中的新事件,特此通知WMS。
ViewRoot 和 WindowManagerService 的管道的文件描述符都是被存储在一个名为InputChannel 的类中,这个InputChannel 类是管道通信的载体。

在创建InputChannel 对的同时,会申请共享内存,并向2个InputChannel 对象中各自保存一个共享内存的文件描述符。

一个管道通信只是对应一个Activity 的事件处理,也就是当前系统中有多少个Activity 就会有多少个全双工管道。
此时系统需要一个管理者来管理以及调度每一个管道通信
因此在创建完InputChannel 对象后,需要将其注册到这个管理者中去。

ViewRoot 端的一般情况下会注册到一个NativeInputQueue 对象中
WMS 端注册在InputManager对象中。

注册到NativeInputQueue
ViewRoot 端InputChannel 对象在向NativeInputQueue注册时,需要注册3个参数:

将InputChannel 对象对应的Native InputChannel 传递给 NativeInputQueue;
将ViewRoot 的成员变量InputHandler 传递给NativeInputQueue,这个InputHandler则是事件的处理函数,传递它的主要作用是明确当前ViewRoot的事件处理函数。
当前Application 的主进程的 MessageQueue

Android 在实现事件传输时,很大程度上借用了线程Looper 和 MessageQueue 的轮询(poll ) 机制,通过它的轮询机制来检测管道上是否有消息通知事件发生,借用Looper机制能够很大限度的保证事件能够第一时间被Application知晓。

注册过程中,android 会将InputChannel 对象中保存的管道的文件描述符交给MessageQueue 的 native looper 去监听,同时向native looper 指示一个回调函数,一旦有事件发生,native looper 就会检测到管道上的数据,同时会去调用指示的回调函数。
handleReceviceCallback()@android_view_InputQueue.cpp

NativeInputQueue 对象,真个系统只有一个,它为了负责管理这么多的Application的事件传递
android 在NativeInputQueue 类中定义了一个子类Connection,每个InputChannel 对象在注册时都会创建一个自己的Connection对象。
registerInputChannel()@android_view_InputQueue.cpp

注册到InputManager

不采用Looper 来轮询是否有事件发生,InputManager 启动了2个进程来管理事件发生与传递,
InputReaderThread 和 InputDispatcherThead, InputReaderThread进程负责轮询事件发生;
InputDispatcherThread 负责dispatch 事件。(如果用一个进程来管理,在轮询input 系统event的事件间隔会变长,有可能丢失事件)。

虽然没有使用Looper 来轮询事件的发生,但是InputDispatcher 使用了native looper(并不是WMS线程的,而是线程InputDispatcher自定义的) 来轮询检查管道通信,这个管道通信表示InputQueue是否消化完成dispatch 过去的事件。

WMS 在初始化时会创建这么一个InputManager 实例,是系统唯一的。
JAVA 层的InputManager 实例并没有实现太多的业务,真正实现Input Manager 业务是Native的
NatuveInputManager实例,它在被创建时,建立起了整个WMS端事件传递系统的静态逻辑。

InputDispatcher
数据传输管理的核心业务是在InputDispatcher中完成的,因此最终WMS端InputChannel 对象会注册到InputDispatcher 中,同样的由于整个系统中InputDispatcher 实例只有一个,而WMS 端InputChannel对象是和ViewRoot 一一对应的。
InputDispatcher 中的Connect类的核心业务是由InputPublisher 对象来实现的,该对象负责将发生的事件信息写入到共享内存。

Notify InputDispatcher表示不同的事件通知InputDispatcher的函数调用,这几个函数虽然是被InputReaderThread调用的,单却是在InputDispatcher定义的。

InputDispatcherThread线程操作
InputDispatcherThread线程的轮询过程dispatchOnce()–>dispatchOnceInnerLocked()。
InputDispatcherThread线程不停的执行该操作,以达到轮询的目的,重点也就是这2个函数处理了。
InputDispatcherThread基本流程
InputDispatcherThread的主要操作是分两块同时进行的,
一部分是对InputReader传递过来的事件进行dispatch前处理,比如确定focus window,特殊按键处理如HOME/ENDCALL等,在预处理完成 后,InputDispatcher会将事件存储到对应的focus window的outBoundQueue,这个outBoundQueue队列是InputDispatcher::Connection的成员函数,因此它是和ViewRoot相关的。
一部分是对looper的轮询,这个轮询过程是检查NativeInputQueue是否处理完成上一个事件,如果NativeInputQueue处理完成事件,它就会向通过管道向InputDispatcher发送消息指示consume完成,只有NativeInputQueue consume完成一个事件,InputDispatcher才会向共享内存写入另一个事件。

事件传递

InputReaderThread 线程操作
当input系统有事件发生时,会被InputReaderThread 线程轮询到
InputReader 会根据事件的device id 来选择的InputDevice
然后再根据事件的类型来选择InputDevice中的InputMapper,InputMapper会将事件信息通知给InputDispatcher

总结输入事件的处理流程

首先从Kernel 传递上来的键值由EventHub进行转码,之后由InputReader将其解释成各个事件,再由InputDispatcher分发。
InputManager是InputReader和InputDispatcher 线程的创建者,它只有一个职责,就是被WindowManagerServic e使用,从Native层获取按键事件。

WindowManagerService 则负责与窗口对接,分发按键消息。

源码位置

事件分发给最前面的窗口:
/frameworks/base/services/java/com/android/server/WindowManagerService.java
拦截消息的处理类:
/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
按键事件定义:
/frameworks/base/core/java/android/view/KeyEvent.java
Java层输入管理:
/frameworks/base/services/java/com/android/server/InputManager.Java
native层输入管理:
/frameworks/base/libs/ui/InputManager.cpp
事件读取线程:
/frameworks/base/libs/ui/InputReader.cpp
事件分发线程:
/frameworks/base/libs/ui/InputDispatcher.cpp
键码与键值转换:
/frameworks/base/libs/ui/EventHub.cpp

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-11 12:31:59  更:2021-08-11 12:34:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 4:38:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码