请尊重分享成果,转载请注明出处:
http://blog.csdn.net/hejjunlin/article/details/52335094

本篇开始分析按键消息事件分发(PS:本篇文章中源码均是android 6.0,请知晓)先看下Agenda:

  • ViewRootImpl中的dispatchInputEvent方法
  • View.dispatchKeyEvent方法
  • ViewGroup.dispatchKeyEvent方法
  • Activity.dispatchKeyEvent方法
  • 按键消息事件时序图

ViewRootImpl中的dispatchInputEvent方法

WMS中接受到消息后,会调用ViewRootImpl中的dispatchInputEvent方法,
如下:
ViewRootImpl.java -> dispatchInputEvent()

下面看ViewRootHandler的handleMessage方法:
ViewRootImpl$ViewRootHandler.java -> handleMessage()

以上获取msg中的event,及receiver后,接着调用enqueueInputEvent方法
ViewRootImpl.java -> enqueueInputEvent()

以上方法,由于默认是false,会执行2:
ViewRootImpl.java -> scheduleProcessInputEvents()

以上方法,再给Handler发一条MSG_PROCESS_INPUT_EVENTS(处理的消息)
ViewRootImpl$ViewRootHandler.java -> handleMessage()

ViewRootImpl.java -> doProcessInputEvents()

上面有个循环操作,就是把QueuedInputEvent传递到deliverInputEvent方法中,主要的作用,就是把上面来的按键消息放到队列中去。
ViewRootImpl.java -> deliverInputEvent()


ViewRootImpl提供一个setView方法,是一个public的,会把mView(也就是DecorView),WMS调用这个方法,注意ViewRootImpl并不是一个View,它实际上是一个Handler,它的作用如下:

向DecorView分发收到的用户发起的event事件,如按键,触屏,轨迹球等事件;(这个是在内部类中实现,下面会说)

与WindowManagerService交互,完成整个Activity的GUI的绘制。

看下setView方法

上面有两个内部类,ViewPostImeInputStage,
ViewRootImpl.java$ViewPostImeInputStage

在看processKeyEvent之前,先看onProcess方法:
ViewRootImpl$ViewPostImeInputStage.java -> processKeyEvent




ViewRootImpl.java -> handleDispatchWindowAnimationStopped()

上面有些代码比较长,了解下就行,主要是明白processKeyEvent方法:
KeyEvent是InputEvent的子类,而InputEvent是一个事件的基类。
处理接收的事件及分发事件的过程,那么问题来了?view或者viewGroup是如何收到按键消息派发下来的呢?ViewRootImpl内部类ViewPostImeInputStage中的processKeyEvent()方法中有这么一段
if (mView.dispatchKeyEvent(event)) {//mView是DecorView(书名),但本质上也是View(乳名)
return FINISH_HANDLED;
}
另外键盘消息派发到view或ViewGroup中,在ViewRootImpl另一个内部类ViewPreImeInputStage中的processKeyEvent()方法中也有这么一段如下:

View.dispatchKeyEvent方法

可以看到从这开始就把按键消息派发到了view中去,然后看view中的dispatchKeyEvent():

KeyEvent.java -> dispatch()


ViewGroup.dispatchKeyEvent方法

再来看下ViewGroup中的dispatchKeyEvent()

上面代码总结为:ViewGroup是重写了View的dispatchKeyEvent,如果有子view时,分发按键消息到子view中去。没有,直接由父view分发。

Activity.dispatchKeyEvent方法

到这就完了么?没有,Activity中dispatchKeyEvent,这不是个重写方法:

以上代码总结为:先让actionbar优先处理keyEvent,然后通过window处理,处理不了,到window上的DecorView处理。window和decorview的关系,相当于一个是窗户,一个是粘在窗户上的纸。所以,decorview可理解为窗户上的纸。Activity的dispatchKeyEvent,是用于处理KeyEvent相关,子类可以重写拦截所以的key event消息在分发到window这一层去的时候,所以我们最好做一些正常的处理流程。
主要过程如下:
1、调用onUserInteraction(),可重载该函数在消息派发前做一些处理
2、回调Activity包含的Window对象的superDispatchKeyEvent,该函数继而调用mDecor.superDispatchKveyEent,该函数继而又调用super.dispatchKeyEvent,DecorView的父类是FrameLayout,而FrameLayout未重载dispatchKeyEvent,因此最终调用ViewGroup的dispatchKeyEvent
3、如果DecorView未消耗消息,则调用event的dispatch()函数,这里的第一个参数receiver是Activity对象

写到这,有一个疑问?就是一个消息怎么从window派发到viewRoot中去呢?或者说ViewRoot中的按键消息是从哪来的?
ViewRoot中有一个内部类: W,W是一个Binder子类(static class W extends IWindow.Stub ),用于接收global window manager的各种消息, 如按键消息, 触摸消息等。 ViewRoot有一个W类型的成员mWindow,ViewRoot在构造中创建一个W的instance并赋值给mWindow(mWindow = new W(this);)。 ViewRoot是Handler的子类, W会通过Looper把消息传递给ViewRoot。

按键消息事件时序图

一张图,总结上面的流程:

第一时间获得博客更新提醒,以及更多android干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码或者长按识别二维码,即可关注。

如果你觉得好,随手点赞,也是对笔者的肯定,也可以分享此公众号给你更多的人,原创不易

更多相关文章

  1. 面试题及答案
  2. Android事件分发机制的探索与发现之ViewGroup篇
  3. 关于Android(安卓)Fragment生命周期以及其他方法的调用执行顺序
  4. Android(安卓)动态创建Drawable selector
  5. Android底层字符传递给上层应用举例
  6. Android(安卓)View的onClick回调机制
  7. 加快Android(安卓)Stduio编译速度方法之一
  8. Android的ViewPager中移除Fragment
  9. 【Android】结束活动退出程序的方法

随机推荐

  1. Android分辨率适配layout布局的问题
  2. Android(安卓)Context 使用时注意内存泄
  3. Android的子线程能更新UI吗?
  4. android图标的制作
  5. 4412开发板Android教程——Android平台简
  6. Android(安卓)Building System 分析
  7. NDK编译Android字符界面的可执行程序
  8. android文本布局引擎
  9. Android(安卓)中文api (81)――InputMeth
  10. 基于RTP和Android的视频传输的研究实现方