Android4.0 input事件输入流程详解(中间层到应用层)

Android4.0 input事件输入流程详解(中间层到应用层)在Android系统中,类似于键盘按键、触摸屏等事件是由WindowManagerService服务来管理的,然后再以消息的形式来分发给应用程序进行处理。系统启动时,窗口管理服务也会启动,该服务启动过程中,会通过系统输入管理器InputManager来负责监控键盘消息。当某一个Activity激活时,会在该Service下注册一个接收消息的通道,表明可以处理具体的消息,然后当有消息时,InputM

大家好,又见面了,我是你们的朋友全栈君。

         Android系统中,类似于键盘按键、触摸屏等事件是由WindowManagerService服务来管理的,然后再以消息的形式来分发给应用程序进行处理。系统启动时,窗口管理服务也会启动,该服务启动过程中,会通过系统输入管理器InputManager来负责监控键盘消息。当某一个Activity激活时,会在该Service下注册一个接收消息的通道,表明可以处理具体的消息,然后当有消息时,InputManager就会分发给当前处于激活状态下的Activity进行处理。

Android4.0 input事件输入流程详解(中间层到应用层)

 

InputManager的启动过程

         InputManager负责事件的监控以及分发,而其启动需要WindowManagerService的启动来完成。而系统启动Win dowManagerService的时候,会执行WindowManagerService.java文件的main方法。

         main方法下会创建一个线程类的实例,并执行其start方法,即调用该线程类下的run方法。run方法里面首先创建一个Looper消息循环,而looperAndroid系统里面处理消息的一种机制,然后调用WindowManagerService的构造方法来启动WindowManagerService

       最后调用Looperloop方法,将该线程添加到一个消息循环里面去,这样系统就可以持续的等待接收并处理到来的消息。

Android4.0 input事件输入流程详解(中间层到应用层)

          

        WindowManagerService的构造方法里,调用InputManager的构造方法创建一个InputManager对象的实例,并将WindowManagerService对象作为参数传入构造方法里面。在InputManager的构造方法里面得到前面创建的looper对象并将其内部的消息队列作为参数传给JNI层的nativeInit函数里面。

        nativeInit函数的实现位于JNI层的com_android_server_InputManager.cpp里面。该函数下主要做两件事情。

Android4.0 input事件输入流程详解(中间层到应用层)

        ◆ 通过java层传入的looper消息队列得到和JNI层的looper对象,是其对应起来

       ◆同样为使和java层的InputManager对象对应起来,创建了本地的InputManager对象实例即NativeInputManager

Android4.0 input事件输入流程详解(中间层到应用层)

        在创建NativeInputManager的过程中,会创建属于JNI层真正地InputManager对象,并将创建的EventHub对象实例当做参数传入到其构造函数里面。(注意这儿传入的EventHub实例)

Android4.0 input事件输入流程详解(中间层到应用层)

       Initialize函数

Android4.0 input事件输入流程详解(中间层到应用层)

        InputManager构造函数里面,会分别创建InputDispatcherInputReader对象的实例并将在创建InputDispatcherThreadInputReaderThread作为参数传入。此时需要注意传入InputReader构造函数里面的eventHubmDispatcher。并且在创建InputDispatcher的时候,创建了一个属于自己的Looper实例。

        此时,InputDispatcherInputReader会通过InputDispatcherThreadInputReaderThread两个线程类来具体的完成事件的分发和读取。到此,InputManager启动完成。

 

应用程序注册键盘消息接收通道

           Activity启动时,系统会为其创建一个ViewRoot实例,并通过其函数setView方法来将有关的view设置到ViewRoot中去,而Activity正是通过setView来注册消息接收通道的。

Android4.0 input事件输入流程详解(中间层到应用层)

         setView方法中会创建一个输入通道InputChannel的对象实例,并作为参数传入到接下来调用的Session类中的add方法中。

Android4.0 input事件输入流程详解(中间层到应用层)

        而该方法正是调用WindowManagerService类中的addWindow方法,且将InputChannel实例作为参数传入到该方法中。

Android4.0 input事件输入流程详解(中间层到应用层)

        此处调用openInputChannelPair方法来创建一个InputChannel类型的数组,而该方法的具体实现是在InputTransport.cpp内完成的。

Android4.0 input事件输入流程详解(中间层到应用层)

         在该方法中,先创建一个服务端的匿名共享内存,可读可写。并将复制一份用于客户端的匿名共享内存,然后通过调用InputChanel的构造函数对服务端和客户端的通道进行实例化。

       其中服务端为反向管道读端与正向管道写端,客户端正相反。这样就可以使客户端和服务端两通道交叉连接,进行消息的传递。

Android4.0 input事件输入流程详解(中间层到应用层)

        继续回到WindowManagerServiceaddWindow方法里面,当创建了两个通道后,需要将各自的通道分别注册到客户端和服务端。

Android4.0 input事件输入流程详解(中间层到应用层)

       registerInputChannel方法即完成对服务端通道的注册。经过层层函数调用,最后是在InputDispatcher.cpp下的registerInputChannel函数完成具体的注册操作的。

Android4.0 input事件输入流程详解(中间层到应用层)

        首先将通道封装在一个Connection对象中,然后获得该通道的读端与Connection一起保存在InputDispatcher中,然后将获得的读端加入到Looper中。

       在Looper类内部,会创建一个管道,然后Looper会睡眠在这个管道的读端,等待另外一个线程来往这个管道的写端写入新的内容,从而唤醒等待在这个管道读端的线程,除此之外,Looper还可以同时睡眠等待在其它的文件描述符上,因为它是通过Linux系统的epoll机制来批量等待指定的文件有新的内容可读的。这些其它的文件描述符就是通过Looper类的addFd成函数添加进去的了,在添加的时候,还可以指定回调函数,即当这个文件描述符所指向的文件有新的内容可读时,Looper就会调用这个hanldeReceiveCallback函数。

Android4.0 input事件输入流程详解(中间层到应用层)

          在对服务端通道注册前,已经将在应用程序层创建的通道与客户端的通道关联起来。回到ViewRoot下的setView来注册客户端的通道。

Android4.0 input事件输入流程详解(中间层到应用层)

        具体的注册实现是在android_view_InputQueue.cpp的registerInputChannel来完成的,和服务端注册大致相同。此时的looper是客户端应用层的looper。应用层创建Looper的时候,会用到一个线程局部变量,sThreadLocal.set(new Looper()),意在保证每个线程内都有一个独立的Looper对象。因此此处的Looper.myQueue即为应用层的消息队列。       

Android4.0 input事件输入流程详解(中间层到应用层)

           到此,服务端和客户端通道已注册完成。

 

 InputManager分发touch消息给应用程序的过程

           在前面的分析中,当InputManager启动完成以后,会在WindowManagerService的构造方法里面执行InputManager对象的start方法。

Android4.0 input事件输入流程详解(中间层到应用层)

         最后会调用InputReaderThreadInputDispatcherThread线程类的run函数来启动两个线程。分别调用其threadLoop函数。

         InputReaderThread->threadLoop调用InputReader->loopOnce函数

         InputDispatcherThread->threadLoop调用InputDispatcher->dispatchOnce函数

         InputReader->loopOnce

Android4.0 input事件输入流程详解(中间层到应用层)

         此处的mEventHub即创建InputManager对象的时候,传入的EventHub实例,而EventHub是一个手机设备的文件描述。当手机设备有事件发生时如有键盘按下时,可以通过其函数getEvents函数获得该事件的具体描述和详细信息。

Android4.0 input事件输入流程详解(中间层到应用层)

         当有事件发生时,会调用processEventsLocked函数来处理,并将对事件信息的详细描述作为参数传入。

Android4.0 input事件输入流程详解(中间层到应用层)

        首先得到所触发事件的类型以及触发此事件的设备Id,然后分别作为参数传入到processEventsForDeviceLocked函数中。

         processEventsForDeviceLocked

Android4.0 input事件输入流程详解(中间层到应用层)

        通过deviceId来得到具体的描述当前设备的InputDevice的对象实例并调用其process函数。

Android4.0 input事件输入流程详解(中间层到应用层)

         手机设备中都有一个和其相匹配的mapper,如果添加一个新的设备,同时会给该设备添加一个mapper的描述,这样当某个设备发生事件时,可以根据与其相匹配的mapper来调用相应的process来处理,在此是以touch事件为例,所以会执行类型为TouchInputMapperprocess函数处理相应的事件。

Android4.0 input事件输入流程详解(中间层到应用层)

         在对应的process里面调用sync,在sync函数下会对touch事件做进一步的处理,然后调用dispatchTouches将触摸事件往上分发。在dispatchTouches里面会对touch事件进行判断,分为三种事件类型,包括DownMoveUp三种,不同的事件类型将以不同的参数调用dispatchMotion继续向上传递。

Android4.0 input事件输入流程详解(中间层到应用层)

Android4.0 input事件输入流程详解(中间层到应用层)

         dispatchMotion里面, touch事件封装在args里面并作为参数传给notifyMotion函数。

Android4.0 input事件输入流程详解(中间层到应用层)

           这里getListener得到的即为mQueuedListener,而在创建该实例时所传入的参数listener即为在创建InputReader对象实例时,传入的InputDispatcher实例。因此此处调用notifyMotion函数,即调用InputDispatcher.cpp下的notifyMotion函数。

       InputDispatcher下的notifyMotion函数:

Android4.0 input事件输入流程详解(中间层到应用层)

         将封装事件的args里面的内容读出来,并重新封装到一个类型为newEntry的对象实例中,并通过调用enqueueInboundEventLocked将事件放入mInboundQueue队列里面。

Android4.0 input事件输入流程详解(中间层到应用层)

         InputDispatcherThread启动时,调用的是InputDispatcherdispatchOnce,而此函数调用LooperpollOnce函数,而当没有输入事件发生时,线程会一直睡在Looper所创管道的读端,此时唤醒即唤醒InputDispatcherThead线程。唤醒后,可以继续执行dispatchOnce函数,

Android4.0 input事件输入流程详解(中间层到应用层)

        调用该函数来完成进一步的操作,

Android4.0 input事件输入流程详解(中间层到应用层)

         把事件从队列mInboundQueue中取出来,然后赋给mPendingEvent,即将事件封装在了mPendingEvent中,并根据其事件类型(以touch事件为例)来调用相应的函数进行处理。

Android4.0 input事件输入流程详解(中间层到应用层)

        首先将mPendingEvent的类型转换,即将事件重新封装在一个类型为MotionEntry的对象实例中,并作为参数传入dispatchMotionLocked函数中去。

Android4.0 input事件输入流程详解(中间层到应用层)

        首先判断当前激活窗口是否存在,若不存在,需要先找出激活窗口,然后调用dispatchEventToCurrentInputTargetsLocked该函数将事件分发给当前激活窗口。

Android4.0 input事件输入流程详解(中间层到应用层)

        首先是得到当前的激活窗口,然后通过窗口所注册的inputChannel得到封装在Server端通道的Connection对象,最后调用prepareDispatchCycleLocked函数继续进行进一步处理,接着调用equeueDispatchEntriesLocked函数。

Android4.0 input事件输入流程详解(中间层到应用层)

         将事件封装成一个DispatchEntry对象,并将其添加到connection的outboundQueue中,表明当前有一个待处理的touch事件。最后调用startDispatchCycleLocked函数来继续分发事件。

Android4.0 input事件输入流程详解(中间层到应用层)

Android4.0 input事件输入流程详解(中间层到应用层)

        首先将事件由队列中取出,将其封装到inputPublisher中,然后调用sendDispatchSignal函数来通知关联的Activity有事件需要处理。publishMotionEvent实际上是把事件的信息放入到一个共享内存中(mSharedMessage)这样该管道的反向读端和正向写端以及匿名内存信息都清楚了。

Android4.0 input事件输入流程详解(中间层到应用层)

        前面在创建管道的时候,Server端和Client端是公用一个匿名内存,管道的前向和反向只是通知相互通知有事件发生了,而真正的事件内容需要去匿名内存里面读取。

Android4.0 input事件输入流程详解(中间层到应用层)

Android4.0 input事件输入流程详解(中间层到应用层)

         Server端通道写端有东西写入,则唤醒主线程进行读取。此时调用handleReceiveCallback函数。

         handleReceiveCallback

Android4.0 input事件输入流程详解(中间层到应用层)

         首先得到Client端封装通道的Connection对象,然后得到InputConsumer对象并调用receiveDispatchSignal判断是否收到输入事件消息的通知。如果收到了通知,调用consume函数。

Android4.0 input事件输入流程详解(中间层到应用层)

 前面已将事件的信息写入了匿名内存,调用consume把事件读出来然后保存在inputEvent

Android4.0 input事件输入流程详解(中间层到应用层)

            将封装在Connection的InputHandle对象取出来。

Android4.0 input事件输入流程详解(中间层到应用层)

         得到javadispatchMotionEvent函数的Id,把事件类型转换为本地类型然后调用CallStaticVoidMethod函数执行java层的dispatchMotionEvent

Android4.0 input事件输入流程详解(中间层到应用层)

        InputQueue.java下的dispatchMotionEvent方法:

Android4.0 input事件输入流程详解(中间层到应用层)

Android4.0 input事件输入流程详解(中间层到应用层)

Android4.0 input事件输入流程详解(中间层到应用层)

Android4.0 input事件输入流程详解(中间层到应用层)

         将事件封装成一个消息,放入消息队列中进行处理( DISPATCH_POINTER)。在handleMessage方法中,若消息类型为DISPATCH_POINTER,则调用deliverPointerEvent方法进行处理。

Android4.0 input事件输入流程详解(中间层到应用层)

         接着调用dispatchPointerEventevent事件传给当前view,然后调用该viewdispatchTouchEvent接口继续向上分发event事件。

Android4.0 input事件输入流程详解(中间层到应用层)

Android4.0 input事件输入流程详解(中间层到应用层)

Android4.0 input事件输入流程详解(中间层到应用层)

         最后调用viewonTouchEvent()接口里面处理event事件,至此,event的传输流程已经讲完了。

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/139846.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 巧用 PHP 数组函数

    巧用 PHP 数组函数

    2021年10月26日
    35
  • android于src和background差额

    android于src和background差额

    2022年1月15日
    40
  • Mybatis 注解开发 + 动态SQL

    Mybatis 注解开发 + 动态SQLHello 大家好我是橙子同学 今天分享注解 Mybatis 注解开发 动态 sql 目录每文一铺垫 今天有小插曲哦 注解开发添加 Insert 删除 Delete 查询 Select 修改 Update 实现结果集封装 Result 实现一对一结果集封装 one 实现多对多结果集封装 Many 动态 SQL 标签 set if 标签 if set

    2025年6月27日
    8
  • 静态路由(静态汇总路由,静态默认路由,负载均衡,浮动静态路由)介绍

    静态路由(静态汇总路由,静态默认路由,负载均衡,浮动静态路由)介绍网络上通过硬件设备传递数据。最常见的就是路由器和交换机。本篇介绍路由器如何使用静态路由条目来转发数据。一个数据包从源IP地址到目标IP地址间可能穿过多个路由器,也可能有多条路径通往目标IP地址。那路由器收到数据后,如何知道哪个端口能通往目标地址呢?如果多个端口都可通往目标地址,又如何选择用哪个端口转发才是最优路径呢?依据的就是路由表。路由表就是路由器的灵魂

    2022年9月25日
    4
  • ICE的服务器对象实现「建议收藏」

    ICE的服务器对象实现「建议收藏」1、需要增加一个类继承至生成的接口类,并实现接口类的虚方法。2、创建实现类的对象3、调用adpater的add方法将创建的对象绑定到adapter中,并传入一个全局唯一标示符,该唯一标示可以通过如下方法生成:  adapter->add(hello,communicator()->stringToIdentity(“hello”));  adapter->addWithUUI

    2022年5月27日
    33
  • 系统分析师零散知识点「建议收藏」

    系统分析师零散知识点「建议收藏」数据库连接池技术是指在系统初期或者初次使用时,完成数据库的连接,以后不再释放此连接,在处理后面的请求时,反复使用这些已经建立的连接。这种方式可以大大减少数据库的处理时间,有利于提高系统的整体性能、可测量性和扩展性。应用服务器的高速缓存?在应用服务器中有页面的缓存和数据库的缓存。页面的缓存是指将特定的URL对应的页面在缓存中予以记录,以便在

    2022年5月24日
    84

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号