首页 > 代码库 > Android View框架总结(九)KeyEvent事件分发机制

Android View框架总结(九)KeyEvent事件分发机制

请尊重分享成果,转载请注明出处:
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干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码或者长按识别二维码,即可关注。

技术分享

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

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    Android View框架总结(九)KeyEvent事件分发机制