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的传输流程已经讲完了。

 

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • Linux 操作memcache命令行

    Linux 操作memcache命令行

  • AUC的公式_蔡勒公式

    AUC的公式_蔡勒公式AUC:随机抽出一对样本(一个正样本,一个负样本),然后用训练得到的分类器来对这两个样本进行预测,预测得到正样本的概率大于负样本概率的概率。开始抽样:在有M个正样本,N个负样本的数据集里。一共有MN对样本(一对样本即,一个正样本与一个负样本)。统计这MN对样本里,正样本的预测概率大于负样本的预测概率的个数。计算:(一)2个正样本:C,D2个负样本:A,B样本对:(C,A)(C,B…

    2022年10月21日
  • pycharmPyCharm 2021.8.3安装激活码【永久激活】

    (pycharmPyCharm 2021.8.3安装激活码)2021最新分享一个能用的的激活码出来,希望能帮到需要激活的朋友。目前这个是能用的,但是用的人多了之后也会失效,会不定时更新的,大家持续关注此网站~https://javaforall.cn/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~KU…

  • centOS 重启 php-fpm

    centOS 重启 php-fpm

    2021年10月19日
  • IOC控制反转的三种方式

    IOC控制反转的三种方式这里写自定义目录标题IOC控制反转三种方式(1).在介绍之前,来了解一下注入的概念:IoC(控制反转)即依赖注入,就是指程序在运行过程中,如果需要另外一个对象协助完成时,无需在代码中创建被调用者,而是依赖外部的注入获取。Spring的依赖注入对调用者几乎没有任何要求,完全支持对象之间的依赖关系的管理。(2).依赖注入通常有两种方式:设置注入、构造注入和属性注入—设置注入:设置注入是通过s…

  • Mybatis的mapper返回map结果集(超详细讲解)[通俗易懂]

    Mybatis的mapper返回map结果集(超详细讲解)[通俗易懂]通过@MapKey指定map的key值1.在接口中写方法/***@author:xjszsd*@date:2021-12-1316:20*/publicinterfaceIAccountDao{//返回一条记录的map;key就是列名,值就是对应的值Map<String,Object>selectAll(Integerid);/***此处若将map的key的类型改为其他类型,不影响@MapKey给map的ke

发表回复

您的电子邮箱地址不会被公开。

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