• Android核心分析(15)Android输入系统之输入路径详解


    Android用户事件输入路径
     
    
    1 输入路径的一般原理
          按键,鼠标消息从收集到最终将发送到焦点窗口,要经历怎样的路径,是Android GWES设计方案中需要详细考虑的问题。按键,鼠标等用户消息消息的处理可分为不同的情况进行判定:
    
    (1)用户输入根据系统状况是否应该派送。如在ScreenOff的情况下,在按键属于特殊按键的情况下等
    
    (2)是否有拦截Listener
    
    (3)对按键事件来讲,是否存在输入法
    
    (4)是否是焦点终点
    
    (5)是否为焦点切换按相关键
    
    这些情况都是设计输入路径需要考虑的基本条件。
    
    1.1一般的输入路径设计
    该输入路径实际上是指的按键消息(MSG_KEYDOWN,MSG_KEYUP, MSG_LongPress)的输入路径,即从活动主窗口到焦点窗口所经历的路程。
    
    
    
    将信息输入路径分为两步:
    
    Step 1)窗口管理器将信息发送到活动窗口
    
    Step 2)活动窗口通过缺省处理函数将该消息一层层的传递到焦点。
    
    这样应用程序可以在活动View的处理函数中来预先处理用户输入信息,从而增强应用对用户信息的控制力。
    
    
    
    传递路径是通过View的缺省处理函数Onxxx来完成。通过ActiveView ->focus->focus->focus的链条关系,一级一级的将按键消息MSG_KEYDOWN,MSG_KEYUP, MSG_CHAR等传递到focus窗口。
    
    
    
    此时用户按键输入先发送到输入法窗口,经过输入法管理器处理,过滤后将输入法产生的结果放置到焦点View。
    
    1.3输入系统整体流程
    下面示意图是Android输入系统的数据流途径,通过WM的输入系统线程收集消息,分发到Focus Activity消息队列,然后通过消息系统派发。
    
    
    
     
     
    2 Android输入路径详细描述
     
    
      2.1 第一步:用户数据收集及其初步判定
         KeyInputQ在WindowMangerService中建立一个独立的线程InputDeviceReader,使用Native函数readEvent来读取Linux Driver的数据构建RawEvent,放入到KeyQ消息队列中。
    
    
    
    preProcessEvent()@KeyInptQ@KeyInputQueue.java这个是在输入系统中的第一个拦截函数原型。KeyQ重载了preProcessEvent()@WindowManagerService.java。在该成员函数中进行如下动作:
    
    (1) 根据PowerManager获取的Screen on,Screen off状态来判定用户输入的是否WakeUPScreen。
    
    (2) 如果按键式应用程序切换按键,则切换应用程序。
    
    (3) 根据WindowManagerPolicy觉得该用户输入是否投递。
    
    2.2 第二步 消息分发第一层面
    InputDispatcherThread从KeyQ中读取Events,找到Window Manager中的Focus Window,通过Focus Window记录的mClient接口,将Events专递到Client端。
    
    
    如何将KeyEvent对象传到Client端:
    
    在前面的章节(窗口管理ViewRoot,Window Manager Proxy)我们已经知道:在客户端建立Window Manager Proxy后,添加窗口到Window Manager service时,带了一个跟客户ViewRoot相关的IWindow接口实例过去,记录在WindowState中的mClient成员变量中。通过IWindow这个AIDL接口实例,Service可以访问客户端的信息,IWindow是Service连接View桥梁。
    
    
    看看在Client ViewRootKeyEvent的分发过程
    
    IWindow:dispatchKey(event)
    
    dispatchKey(event)@W@ViewRoot@ViewRoot.java
    
           ViewRoot.dispatchKey(event)@ViewRoot.java
    
                         message>
    
                         sendMessageAtTime(msg)@Handler@Handler.java
    
    至此我们通过前面的Looper,Handler详解章节的分析结论,我们可以知道Key Message已经放入到应用程序的消息队列。
    
    2.3第三步:应用消息队列分发
       消息的分发,在Looper,Handler详解章节我们分析了Looper.loop()在最后后面调用了handleMesage.
    
              …
    
               ActivityThread.main()
    
                    Looper.loop()
    
                      ViewRoot$RootHandler().dispatch()
    
                          handleMessage
    
                              ....
    
          注意到在分发的调用msg.target.dispatch(),而这个target在第二层将消息sendMessageAtTime到消息队列时填入了mag.target=this即为msg.target=ViewRoot实例。所有此时handleMessage就是ViewRoot重载的handleMessage函数。
    
    handlerMessage@ViewRoot@ViewRoot.java
    
           deliverkeyEvent
    
                如果输入法存在,dispatchKey到输入法服务。
    
                否则deliverKeyEventToViewHierarchy@ViewRoot.java
    
         在这里需要强调的是,输入法的KeyEvent的拦截并没有放入到Window Manager Service中,而是放入到了客户端的RootView中来处理。
    
    2.4第四步:向焦点进发,完成焦点路径的遍历。
    
    
    分发函数调用栈
    
    deliverKeyEventToViewHierarchy@ViewRoot.java
    
    mView.dispatchKeyEvent:mView是与ViewRoot相对应的Top-Level View.如果mView是一个ViewGroup则分发消息到他的mFocus。
    
    mView.dispatchKeyEvent @ViewGroup  (ViewRoot@root)
    
                   Event.dispatch
    
                            mFocus.dispatchKeyEevnet
    
        如果此时的mFocu还是一个ViewGroup,这回将事件专递到下一层的焦点,直到mFocus为一个View。通过这轮调用,就遍历了焦点Path,至此,用户事件传递完成一个段落。
    
    2.5第五步 缺省处理
    如果事件在上述Focus View没有处理掉,并且为方向键之类的焦点转换相关按键,则转移焦点到下一个View。
  • 相关阅读:
    HDU 1874 畅通工程续(dijkstra)
    HDU 2112 HDU Today (map函数,dijkstra最短路径)
    HDU 2680 Choose the best route(dijkstra)
    HDU 2066 一个人的旅行(最短路径,dijkstra)
    关于测评机,编译器,我有些话想说
    测评机的优化问题 时间控制
    CF Round410 D. Mike and distribution
    数字三角形2 (取模)
    CF Round410 C. Mike and gcd problem
    CF Round 423 D. High Load 星图(最优最简构建)
  • 原文地址:https://www.cnblogs.com/wanqieddy/p/2512910.html
Copyright © 2020-2023  润新知