Android 5.1触摸事件的传递机制深度剖析(上)
在Android的View系统中,TouchEvent消息的传递是非常重要的,只有深入TouchEvent消息的传递机制才能更好的理解一些View组件的原理和编写自定义View控件。本文基于Android 5.1源码进行分析。
Android系统的消息处理过程,大致可以如下
触摸消息 ---> 消息处理前端 ---> 窗口管理系统 ---> 应用窗口
当用户通过触摸屏等输入设备产生触摸信息,该消息会首先被消息处理前端转换为更明确的消息,比如DOWN/UP消息。然后通过窗口管理系统(WindowManagerService)根据消息的位置坐标去匹配所有的窗口,判断该坐标的落地窗口的哪个区域中,则把该消息下发给相应的窗口。最后窗口把该消息下发给根View,如果根View没有消费该消息则下发给子View,这样层层传递下去。
从源码开始分析触摸消息的传递机制
Let’s reading the fucking souce code.
首先从Activity开始分析,看WindowManagerService是如何将消息分发给应用层的。
Activity它有两个关键点
Window mWindow
成员变量,Window是一个顶级窗口抽象类,它的实现类是PhoneWindow
,在Activity的attach()
函数中通过mWindow = PolicyManager.makeNewWindow(this)
获得,PhoneWindow有一成员变量DecorView mDecor
实现了
Window.Callback
接口,它包含了若干事件回调函数,其中就有dispatchTouchEvent(MotionEvent event)
函数,并在Activity的attach()
函数中通过mWindow.setCallback(this)
注册了回调
DecorView它继承于FrameLayout,它是Activity的顶级Layout,通过
setContentView()
加入到Activity的View其实都是被加入到该Layout中,它负责了触摸消息的向下分发ViewRootImpl在Android3.0以前叫ViewRoot,它不是一个View,它是用来沟通WindowManagerService,它想wms中注册了消息接收通道(InputChannel)处理来自底层的事件消息。这些都在函数ViewRoot.setView中完成
|
|
setView()
方法首先将对mView
赋值,然后调用requestLayout()
函数来通知InputManager,这个Activity窗口是当前被激活的窗口,最后当窗口属性没有INPUT_FEATURE_NO_INPUT_CHANNEL
标志时创建mInputChannel
并通过mWindowSession.addToDisplay()
函数注册通道。
在RootViewImpl中的函数通道是各种策略(InputStage)的组合,各策略负责的任务不同,如SyntheticInputStage、ViewPostImeInputStage、NativePostImeInputStage等待,这些策略以链表结构结构起来,当一个策略者没有消费事件时,就传递个下一个策略者。其中触摸事件由ViewPostImeInputStage处理。
|
|
在onProcess()
函数中如果事件不是按键事件且是触摸点事件则调用processPointerEvent()
函数,在该函数中调用mView.dispatchPointerEvent()
函数来下发该事件
DecorView是PhontWindow的内部类,在DecorView的dispatchTouchEvent()
的函数实现中可以看到它直接调用了callback的回调函数,而这个函数的实现就是Activity了
|
|
通过对RootViewImpl的分析,我们知道了触摸事件的下发关键在与mView这个对象,那mView这个对象是在何时被赋值的呢?
它在ActivityThread.handleResumeActivity()函数中被赋值的,首先获得Window对象r.window = r.activity.getWindow()
,然后再通过该Windowd对象的r.window.getDecorView()
函数 获得顶级View,最后通过WindowManager的实例对象的wm.addView(decor, l)
函数将View添加到窗口中
|
|
再来看WindowManager实例中的addView()
函数,它直接调用了WindowManagerGlobal的addView()函数,从这能看出,WindowManagerGlobal才真正是WindowManager的实现类,WindowManagerImpl只不过代理了一下
|
|
最后,WindowManagerGlobal的addView()
函数中直接new了一个ViewRootImpl对象,然后在这个对象上直接调用了ViewRootImpl的setView()
函数,就这样把Activity的顶级View赋值给了它的成员变量
|
|
至此,已经完全分析了Activity是怎么通过ViewRootImpl这个对象来接收底层的事件消息,下一节中,我会对从Activity出发,进一步分析事件的传递机制。
本人才疏学浅,有分析错误的地方,还请大家纠正。另外,本文中,有些逻辑分析是来自老罗的blog,非常感谢老罗,他写的Android文章十分有用,大家也不妨去看看。
Android应用程序键盘(Keyboard)消息处理机制分析
原创不易,欢迎转载,但还请注明出处:waynell.github.io