成鹏致远|2013-10-25

Linux输入子系统回顾

1:为什么要回顾linux输入子系统?这个问题后面自然就知道了

1.linux输入子系统设备是基于平台设备机制的,所以先回顾平台设备机制,主要回顾后面用得到的东西

1.申请主设备号
2.创建cdev->将cdev挂载到系统设备哈希链表中,同时生成inode节点
3.创建device->将device与刚生成的inode节点关联起来,为上层调用提供接口

2.注册输入子系统设备

1.创建一个设备类class
2.申请主设备号
3.创建cdev->将cdev挂载到系统设备哈希链表中,同时生成inode节点
4.创建input_device->将input_device与刚生成的inode节点关联起来,为事件驱动层提供接口

1.注册设备支持输入事件类型(type)->【这个后面会用到】
2.注册设备支持输入事件编码
3.生成/dev/input_device

      5.当input_device与input_hander匹配成功

1.生成/dev/input*
2.上层应用通过主设备号打开/dev/input,通过次设备号打开/dev/input_device

3.输入子系统部分函数

1.read函数->读【后面会用到】
2.write函数->写【后面会用到】
3.event函数->上报事件【后面会用到】


【提前思考的问题】

1.Android事件处理系统是怎么捕捉到输入事件的?
2.Android上层应用是怎么捕捉到输入事件的?
3.Android上层应用捕捉到输入事件是怎么响应的?


2.从启动一个Android程序开始

  1.Activity启动流程:onCreate()->onStart()->onResume()->Activity Running

【Q1】:为什么onStart()后不直接Running,要插入一个onResume(),在onResume()中系统作了什么事情?
<A1>:在onResume()中,系统会为该Activity创建一个ViewRoot!

【Q2】:这个ViewRoot有什么用?它做了哪些事情?这个后面解答


3.Android事件从输入到输出的整个流程

  1.Activity运行时,用户点击触摸屏操作->{事件产生
    1.点击触摸屏,必然会调用触摸屏驱动->{事件输入

【Q3】:事件传递过程?->{事件传递}
<A3>:以下所有步骤!
 1.Android上层应用调用Framework层的JNI本地方法->{事件消息传递到JNI层

 1.实现JNI层方法,填充本地方法映射表,实现提供Android上层应用的接口
 2.生成so动态链接库文件,adb push到/system/lib目录下

 2.JNI本地方法调用HAL层(硬件抽象层:中间件)方法->{事件消息传递到HAL层

 1.JNI层通过指定ID得到HAL模块实例,然后调用HAL层函数
 2.生成so动态链接库文件,adb push到/system/lib/hw目录下

 3.HAL通过系统调用write进入内核层->{事件消息传递到内核

 1.copy_from_user()取得用户层数据
 2.调用input_device.write->{(1.1.2):事件消息传递到驱动层

------------------------------------------------------------------------------------------
 4.输入设备驱动获取事件,调用event函数->{(1.1.3)}
 5.事件处理层根据事件类型进行上报->{事件消息传递到内核事件处理层
   1.调用input_event(device,type,code,value)->{【type】后面会用到}
 6.到事件处理层时,内核会唤醒read函数->{(1.1.1):事件消息传递到内核层
   1.通过copy_to_user()将内核数据传递到用户空间->{事件消息传递到用户空间
【Q4】:read函数被谁调用的?
 7.事件消息被Android的事件处理系统捕捉到->{事件消息传递到Android事件处理系统

 1.Android事件处理系统将这个消息发送到Android应用层
【Q5】:事件消息在Android事件处理系统中是怎么传递的?

 8.Android上层获取事件消息,根据事件类型(3.1.1.5.1:type)响应上层View相应回调函数
 9.Android界面UI更新->{事件响应


4.至此为此,项目中用到的框架知识已经介绍完毕

  1.Android事件处理系统作为一个黑盒子,暂时略过
  2.切入到项目解读:×××远程遥控器

1.客户端APK

1.数据采集->{此数据必须与Linux输入子系统兼容,这样才能达到欺骗系统的目的}
2.数据传递

2.服务端APK->{实现二个虚拟设备(虚拟键盘设备与虚拟鼠标设备}

1.接收数据
2.将数据传送到虚拟设备
3.虚拟设备上报事件,欺骗系统输入事件发生


5.剖析Android事件处理系统

【Q5】:事件消息在Android事件处理系统中是怎么传递的?
<A5>:看看Android的事件处理系统
1.紧接着3.1.1.7,事件消息传递到Android事件处理系统
2.在Android开机时,系统服务(System Server)会初始化Android窗口管理服务(WindowManagerService
3.WMS服务会初始化InputManagerInputManager是Framework层的一个C++类,负责管理所有的输入事件的捕获与转发

1.在InputManager构造函数中,会初始化事件处理系统中最重要的三个类(InputReader/InputDispatcher/EventHub
2.然后初始化InputManager(InputManager.initialize()),产生两个线程(InputReaderThread/InputDispatcherThread,分别负责事件捕获与事件转发)
3.InputReaderThread线程工作->{解决问题1:Android事件处理系统是怎么捕捉到输入事件的}

    1.调用InputReader::loopOnce(),在InputReader类中,已经实现looper机制,循环操作
      1.调用EventHub::getEvent()函数
        1.调用Epoll_wait()函数
          1.在Epoll_wait()函数中,会去循环读取所有的/dev/input设备,一旦有事件产生,就会被此函数捕捉到

【Q6】:Android事件处理系统与/dev/input设备是如何关联起来的
A6】:EventHub::Device <---->/dev/input,一一对应关系

 2.当有事件发生时,Android事件处理系统会调用read函数->{系统调用,从内核层拿到事件消息}

【Q4】:read函数被谁调用的?
<A4>:read函数是InputReader调用的

      2.将从内核层读取到的事件消息(input_event)保存到RawEvent中

2.根据事件类型(input_event.type)调用相应的消息转换器(InputMapper
  1.Android系统目前支持5种事件消息(翻滑盖/轨迹球/多点触摸/单点触摸/键盘)
3.InputReader::InputDevice::inputMapper将RawEvent事件转换成对应的Notifyargs
  1.InputMapper将RawEvent事件转换成对应的Notifyargs类
  2.将Notifyargs加入到InputRead::QueueListener::argsQueue队列中
4.调用InputReader::QueueListener::flush()函数,处理队列中的事件消息
5.调用Notifyargs::notify函数,将事件消息转换成对应的EventEntry再转交给InputDispatcher

1.对事件进行预处理
  1.判断是否丢弃此事件消息
    1.丢弃,则直接返回
    2.不丢弃
  2.获取当前Activity对应的Connection对象->(这个后面再讲)
2.将对应的EventEntry加入到InputDispatcher::OutBoundQueue队列中
【Q7】:在Notifyargs是怎么获取InputDispatcher实例的
  <A7>:在初始化InputRead::QueueListener::argsQueue队列时,将InputDispatcher对象传递过来了

6.唤醒InputDispatcher::pollOnce()函数

4.InputDispatcherThread线程工作
  1.调用InputDispatcher::loopOnce(),在InputDispatcher类中,同样实现looper机制,循环操作
    1.调用pollOnce()函数,轮询InputDispatcher::OutBoundQueue队列
      1.队列中的有事件消息
      2.被Notifyargs::notify()函数唤醒
  2.调用对应的InputDispatcher::dispach函数
    1.从InputDispatcher::OutBoundQueue队列中取得EventEntry对象
    2.将EventEntry对象转换成DispatchEntry对象
    3.将DispatchEntry对象加入到InputChannel::Connection::outboundQueue队列中
【Q8】:这个InputChannel::Connection对象是从哪里来的?有什么用?
【Q9】:现在InputDispatcher得到了转换后的事件消息,即将要发出去,但是往哪里发?
<A9>:要解决这个问题,需要回到【Q2】


4.【Q2】:ViewRoot有什么用?它做了哪些事情?->{解决问题2:Android上层应用是怎么捕捉到输入事件的?
  <A2>:1.首先,ViewRoot是一个Hander,与当前Activity绑在一起的
      2.ViewRoot有一个重要的作用:与WMS通信,完成整个GUI窗口系统的绘制
      3.创建ViewRoot的时候做了哪些事情?现在来解决问题2:Android上层应用是怎么捕捉到输入事件的?

1.由前面分析知道,Android的事件输入来自InputManager,所以ViewRoot需要与InputManager通信
【Q10】:ViewRoot怎么与InputManager通信并取得事件消息?
  <A10>:ViewRoot与InputManager之间有一个共享内存(ShareMemory
    1.InputManager::InputDispatcher将最后的事件消息发送到共享内存(ShareMemory)中
    2.ViewRoot在知道有事件消息到来时,就去共享内存(ShareMemory)中取此事件消息
【Q11】1.ViewRoot怎么知道有事件消息到来?
  <A11>:1.ViewRoot与InputManager是通过管道通信的机制来传递消息的
    1.在创建ViewRoot后,会创建两个InputChannel类对象
      1.其中一个InputChannel对象注册到NativeInputQueue中,与ViewRoot绑在一起
【Q12】这个NativeInputQueue是用来干什么的?
        :这个NativeInputQueue是Android系统用来维护事件接收的,因为同一时刻,会有很多Activity在等待事件输入
      2.另一个InputChannel对象注册到InputManager类对象中
    2.同时会申请上面用到的的共享内存
    3.InputChannel类主要封装管道描述符和共享内存的描述符等信息
      1.在ViewRoot与InputManager中各注册了一个InputChannel类对象,其中各有两个管道描述符
        1.两个InputChannel对象中都包含一个读和一个写描述符
        2.所以在ViewRoot与InputManager之前完成了全双工的通信【后面会用到】
【Q13】:在Android中,每创建一个Activity,就会创建一个ViewRoot,所以也会创建一个InputChannel对象,那Android系统怎么来区分这些Activity?
  <A13>:还记得【Q8】吗,两个问题一起解决:InputChannel::Connection对象是从哪里来的?有什么用?
    1.为了区分不同的Activity,NativeInputQueue类中定义了一个子类Connection
    2.在注册InputChannel对象时,每个InputChannel对象中都创建了一个Connection对象
      1.所以ViewRoot中的每个InputChannel与InputManager中的InputChannel都包含一个Connection对象
      2.这个Connection对象标识了不同的Activity

        4.到现在我们就可以回到【Q9】了

5.【Q9】:现在InputDispatcher得到了转换后的事件消息,即将要发出去,但是往哪里发?
 <A9>:1.由上面的分析,我们知道现在需要将事件消息发送给ViewRoot
    1.调用InputChannel::Connection::inputPublisher.publishMotionEvent函数将事件消息发送到共享内存(ShareMemory)
    2.InputChannel对象向写管道发送一个dispatch信息

6.现在工作就该转移到ViewRoot这边了
【Q14】:在NativeInputQueue中有很多InputChannel对象,究竟哪个InputChannel的管道会收到信息?
  <A14>:还记得【Q13】吗,InputManager的InputChannel对象是由ViewRoot创建后注册过去的
    1.在每个注册的InputChannel对象中,都包含了一个Connection对象
    2.InputManager的InputChannel对象的Connection对象<--->NativeInputQueue中的InputChannel对象的Connection对象,是一一对应的关系
    3.所以与InputDispatcher中Connection对象相对应的那个Connection对象将收到管道信息
  1.相应的InputChannel对象的Connection对象收到管道信息
  2.调用InputChannel::Connection::inputConsumer到共享内存(ShareMemory)中取得事件消息
  3.InputChannel再向写管道中发一个信息,表示此事件已经取得
  4.然后InputManager中的InputChannel将会收到管道消息,再继续进行下一轮事件处理

7.到此为止,事件消息已经传递到NativeInputQueue中的InputChannel对象中
  1.由前面分析,NativeInputQueue中的每个InputChannel对象都对应一个Activity
  2.NativeInputQueue中InputChannel对象是在ViewRoot创建之后创建的,也就是我们的Activity启动之前
【Q15】:现在Activity关联的InputChannel对象拿到的这个事件消息,但是怎么处理呢?现在我们就来回答问题3:Android上层应用捕捉到输入事件是怎么响应的?
    <A15>:这个需要回到ViewRoot端InputChannel对象注册的时候,也就是【Q11】的位置
      1.ViewRoot端InputChannel对象在向NativeInputQueue注册时,需要注册3个参数
        1.其中有一个参数就是ViewRoot的成员变量InputHandler
          1.InputHandler就是事件的处理函数,也就是所谓的回调函数
          2.传递它的作用主要是明确当前ViewRoot的事件处理函数
          3.当InputChannel对象取得事件后,就会去调用ViewRoot的InputHandler函数-{到此为止,事件消息就传递回了Android应用层}
【Q16】:这个回调函数到底是什么?
            <A17>:这个回调函数大多被Android系统实现成抽象函数
              1.在我们的×××远程遥控器的客户端,就重写了对应的方法,用来获取我们需要的数据,比如onScroll/onLongPress等



思维导图


更多相关文章

  1. Android中Intent对象与Intent Filter过滤匹配过程详解
  2. Android 基于回调的事件处理----事件分发
  3. Android 软键盘监听事件
  4. Android 中触摸事件的分发和拦截
  5. Android中的事件处理机制
  6. Android的事件处理模式
  7. 源码角度分析Android的事件输入系统(input system)及ANR原理分析
  8. Android NDK开发——操作JNI函数以及复杂对象的传递

随机推荐

  1. Android 如何有效修改包名
  2. android studio 编译打包包含jni的sdk需
  3. Android(安卓)HIDL lshal
  4. Android(安卓)Gallery组件详解
  5. Android 屏幕刷新机制:ViewRootImpl、Chor
  6. 解决onConfigurationChanged不被调用
  7. Android DataBinding & MVVM
  8. if(Build.VERSION.SDK_INT>=Build.VERSIO
  9. Android(安卓)Sensor HAL层分析
  10. Android开发の开发环境搭建