为了账号安全,请及时绑定邮箱和手机立即绑定

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

标签:
Android

本篇开始分析按键消息事件分发(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。

按键消息事件时序图

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

这里写图片描述

点击查看更多内容
12人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
移动开发工程师
手记
粉丝
1.7万
获赞与收藏
87

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消