文档章节

关于ANDROID事件-Android框架工程师的回复

刘小兵2014
 刘小兵2014
发布于 2012/11/09 19:34
字数 3080
阅读 242
收藏 3
Event dispatching is relatively simple.  There are two major models:

- Focus-based dispatching is used for key events and trackball events.
- Position-based dispatching is used for touch events.

In all cases, the dispatching you probably care about starts with an IPC coming from the window manager into your process, telling a specific window about an event.  This is received in the internal VIewRoot class.  This in turn simply calls the appropriate dispatchXxx() function on the top-level view of the hierarchy, which dispatches it down to child views based on either the current focus target or the x/y coordinate of the event as appropriate.  Most of the code for dispatching down the hierarchy is in ViewGroup.  Ultimately View will receive the dispatchXxx() call and turn it into an onKeyDown() etc.  The View class and its subclasses will also in variation situations look at the raw events they are receiving to perform higher-level callbacks like onClick().

Another important common pattern we have for low-level events is returning a boolean indicating whether the receiver handled it.  If you return false, the framework will try to perform a callback on the next target.  For both focus and position dispatching, this means doing a callback on the parents up the view hierarchy until someone handles it or we reach the top.  In addition, returning true on a down of a touch event "locks in" that view as the target, allowing to receive the following moves and final up event regardless of where they occur on the screen; if you return false you will not receive any more events until the next down.

We don't use any messaging for dispatching an event and the corresponding actions that result -- this is all done through callbacks -- so in general if you break into the debugger from a callback you can see the primary chain from receiving the original raw event in ViewRoot through to the call you are getting.

And of course you can look at the source code to see the actual implementation.  There isn't a -lot- to deal with -- ViewRoot for the initial receipt of the event, the dispatch functions on ViewRoot for deciding where the event goes, View for the low-level callbacks, and the appropriate class for higher-level callbacks.
- show quoted text -


On Mon, Feb 2, 2009 at 10:37 PM, Craig <craig.b...@gmail.com> wrote:

Hi Dianne,

One of the persistent problems I've had over the years developing
GUIs, whether it be MFC, wxWndows, Swing, or now Android, has been
understanding the exact flow of events within the framework. All is
well if simply overriding onItemClick() is enough to get the job done.
But as soon as an app starts getting complicated, and I need to do
something a little different it can become a lesson in frustration
trying to get that event to fire in the right place at the right time.
Once you put class inheritance (i.e. overrides and handler functions),
view hierarchies (i.e parent-child relationships), and runtime
listeners, all in the pot, it's anyone's guess where that event will
end up, or how best to do what you want. Given the framework soure
code, you can try and follow the chain of events, but that is often
impossible; without it, well, google is your friend.

One example of frustration from my Android experience was trying to
get checkbox views clicking independently of a list view row (i.e.
*not* like the CheckBoxPreference), and still behaving normally in all
other respects.

So my question: is there a high level overview of the framework design
philosophy, how it fits together, and specifically, the routing
algorithms for events. I think I've read the sdk docs inside out now
and I haven't seen one. It would be very helpful.

thanks and regards,
Craig




--
Dianne Hackborn
Android framework engineer
hac...@android.com

Note: please don't send private questions to me, as I don't have time to provide private support.  All such questions should be posted on public forums, where I and others can see and answer them.

Android的事件比他讲的复杂. 比如touch事件. Android的touch事件除了他讲的这些,至少还有另外两样东西不能miss: 1,viewgroup 的事件拦截; 2, listener与view本身的ontouchevent的关系.

关于第一个, 其实相对独立并且局部性比较强, 基本上与全局的事务处理没关系. 但第二个是很多人容易陷入误区的地方. 因为在整个Android事件处理的过程中(按照他说的, 全在callback里面也就是android的事件处理全部都是通过代码调用进行的), 到处都是对事件处理结果(return value)的返回. 比如dispatch, 比如onintercept, 比如ontouchevent, 比如listeners中的返回. 所以有必要对这些概念进行一下梳理,不然不可能对它有个清晰的印象就更别提使用对它的了解来解决问题了!

首先的基础概念是View与Listener. 这个是基本中的基本. 因为Viewgroup只不过View的另一种形式, 是View的衍生品. 或者说, 事实上, 在Android系统里面, 真正拥有事件处理能力的就三样东西: View, Listener, Activity. 至于如GestureListener, ScaleGestureListener等等, 甚至ClickListener等, 已经全部都是挂在原始即onTouchListener或onKeyListener等上面的"衍生"Listeners, 在讨论Android事件结构的时候 , 完全可以先放一下不讨论(不过在最终总结的时候要顺便把它进行一下归结).

事件一来, 首先到Activity的dispatchTouchEvent. 事实上, 这个逻辑放到View与ViewGroup上同样成立. 也就是说, 所有的触摸事件都是通过dispatchTouchEvent发送出去的. 所有的事件处理元素通过dispatchTouchEvent接收事件, 也通过dispatchTouchEvent返回事件处理结果. 所有的事件处理都是在这个方法中完成的, 不管在哪个类中. 至于Listeners与onTouchEvent, 则都是通过它才可能被调用的.

这样一来就清楚了, 即: 如果不考虑intercept即事件拦截的话, 所有的事件都遵循相同的途径: Activity ->ViewGroup->View. 并且这个途径指的是代码调用的路径, 不是指具体的执行路径. 而代码调用, 稍微研究过堆栈的都知道, 有两个过程: 一个是栈的开劈, 一个是栈的回收. 这个跟Activity的堆叠方式有点象(这里面其实包含我另一个研究结果: Activity在很大程度上是Android系统中的一个非常优秀的内存节约设计, 因为它的引入给系统提供了使用中间结果进行操作而节省了深度堆栈调用所必需的deep Stack---试想每一个应用都拥有一个长得不能再长, 并且不知道什么时候才能彻底回收的堆栈. 这就是PC上的情况. 在PC上面, 所有的应用都是一大陀混合交接在一起的函数. 而且函数中保存的并不都是用户的操作状态而是函数自己的状态----这些东西, 在PC上被视为"工作").

开辟与回收走两条完全相反的路, 这个跟事件处理的结果无关. 也就是说, 不管具体到每一个View上的处理结果怎么样, 函数调用都是这么工作的. 但是根据不同的返回值, 函数可能执行不同的行为: 比如一个Viewgroup的叶子结点View返回了一个为true的处理结果, 这个ViewGroup的dispatchTouchEvent方法就会认为当前对于这个事件的处理已经结束所以提前返回而不是进一步去调用假想路径(当所有的函数都返回false时此事件需经过的路径)中的下一个节点:其自身的onTouchEvent. 并且这个逻辑也同样的可以应用在Activity上, 这一点也可以从Activity的代码(dispatchTouchEvent)中看出来.

关于拦截, 是这么理解的: 如果它的确拦截了, 那么它就是拦截了. 具体的处理结果依赖于拦截对象本身的onTouchEvent方法的返回值, 如果它返回true, 那么此拦截者本身的dispatchTouchEvent方法自然也就返回true, 所以导致其本身的调用者也认为它已经完成了当前事件的处理所以向更上一级返回结果并维持这样的处理方式直到Activity. 并且Activity在收到这样的返回值后也将做同样的动作: 即向上汇报--此事件已经被正确处理或者说被消费了.

由此可见, 在建立正确的Android事件处理的几个基本概念即对象以后, 唯一重要的两样东西即两个: 一是假想的调用途径; 一是值的返回.

讲到这里要插一下Listener与onTouchEvent的关系. 注意这里讨论的两者都是Android事件系统中的核心元素而非衍生如ClickListener或GestureListener等东西, 因为这些东西都是要么挂在原生Litener上, 要么就挂在onTouchEvent上处理的东西所以讨论它们是没有必要的. 只要搞清楚了前两者的关系, 后面的就都清楚了.

Listenr与onTouchEvent的关系其实很简单, 在View类的dispatchTouchEvent方法中先调用前者后调用后者. 至于谁更重要这个问题其实也不存在. 重要的是要把它们俩象前面一样也纳入整个事件处理的假想途径就可以了. 因为它们在代码上的关系就是这样的. 它们虽然是dispatchTouchEvent方法内部的代码, 但是却拥有与dispatchTouchEvent方法同样的地位! 为什么这么说呢? 你可以把它们看成两个分开执行的方法, 其结果与把它们放在一起处理是一样的. 完全等价. 这就是原因!

这是从事件处理链条的角度看的结果. 如果从View本身看就有点不同: 如果Listener(核心即onToucListener)返回true, 那么View本身的onTouchEvent根本就没有执行的机会. Android的官方文档也是这么说的: 在大多数时候这时应该返回一个false. 否则, 很有可能发生的是: 比如说这个View是个按钮, 它便连Click的表现都不会有("沉下去"), 就更别提被调用然后生产出一系列其它衍生事件如onClick, onGesture(比喻.并没有真正的这样的方法)等等啦.

写到这里突然意识到一件事那就是对Listener的理解. Android的官方文档好像从没有对它进行过解释或分类. 我觉得他们是有意在隐藏这里面的复杂性 (事实上, 我认为Android系统中最复杂的或者说唯一复杂的就是它的事件处理)以吸引开发者进入他们的开发社区(Which I think it's completely unnecessary!). Anyway, android里面的Listener多如牛毛, 但是基本的Listener就三个: onKeyListener, onTouchListener, onTrackballListener. 所有其它的事件或Listener都来自这里面. 或者说, 其实Android本身也没有为这些Listener关联或命中任何的"事件". 因为从头到尾人家提到的只是Listener, 是你自己联想翩翩才假想所有的Listener必有对应的Event,然而实际上并不是. 真实情况是, Android会对前面所述三种原始事件进行处理识别然后Fire(也没有对应的函数, 这里只是借用一下用来表达对应代码的语义. 因为其实Android所用的词是perform)一系列的方法其中就包括调用onClickListener的方法. 但是要注意的是人家从头到尾并没有提及什么事件. 所有提及事件的地方都在其核心元素与核心方法中.

只有去除了对Listener的神化色彩之后, 你才可能真正理解Android的事件"框架". 说它是框架, 是因为它实在不是一个象样的设计. 甚至你根本看不到设计的痕迹. 它就是一大堆调用, 然后根据不同的返回值执行不同的行为.

如果你去看Android的源码, 你会发现一旦把很多的Listener都Rule out你的考察范围的话, 事情马上变得简单很多. 实际上, 真正需要考察的就只剩下三个方法: View类的dispatchTouchEvent, onTouchEvent, ViewGroup类的dispatchTouchEvent. 按照上面工程师的说法, 最复杂的东西其实都在ViewGroup类中的dispatchTouchEvent中. 这个类实际上基本上隐藏了所有的Android事件逻辑也非常难理解. 我看了三天依然没有完全看懂或一点点明白它的逻辑. 方法说明也没有提供任何详细说明.但是你要真想搞明白Android的事件机制, 这个方法是肯定不能丢的. 虽然经过上面的阐述它的大体框架是清楚了: 比如它的假想路径;它的遇true退出机制(不管是在dispatch还是在onTouch或onTouchEvent方法中, 任何方法返回true都将导致如多米诺骨牌般的退出效应---庞大的事件处理链条瞬间中止并迅速返回);它的Listener(原始)优于View自身的事件处理函数如onTouchEvent处理机制, 但是具体细节还不清楚.

© 著作权归作者所有

共有 人打赏支持
上一篇: Android与线程
下一篇: Android程序员
刘小兵2014
粉丝 65
博文 532
码字总数 465927
作品 0
深圳
程序员
私信 提问
Android知识体系总结(全方面覆盖Android知识结构,面试&进阶)

Android知识体系总结(全方面覆盖Android知识结构,面试&进阶 Version-1.0.1 时间:2018.09) 基本内容 : Android基础知识:基本涵盖Android所有知识体系,四大组件,Fragment,WebView,事件分发...

Java高级架构
11/01
0
0
【必看】Android干货整理

哗啦啦,为方便大家更好的学习交流,小编特地整理了一大波干货!预备 前方高能预警,一大波干货袭来,接住了!!!!!!!!!!!! 如果你是零基础小白,不知如何上手Android开发,不知应学习哪些工具...

慕课网官方_运营中心
08/01
0
0
高手问答第 216 期 —— Android 开发应该如何进阶?

OSCHINA 本期高手问答(2018 年 11 月 7 日 — 11 月 13 日)我们请来了@刘望舒 为大家解答关于 Android 开发方面的问题。 刘望舒,资深开发工程师,Android 进阶二部曲《Android进阶之光》和《...

局长
11/07
0
0
基于 MVP 架构使用Android通用开发框架快速开发微博项目实战

课程目标: 基于 MVP 架构使用Android通用开发框架快速开发微博项目实战 适应人群: 适合大学生和初中级android开发工程师,可以系统化的微博类APP的开发,系统化掌握商业化项目的开发。* 不...

13122542396
05/25
0
0
Android开发_推荐一些顶级Android书

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangty0223/article/details/9386813 这两天在网上看到的一些perfect android books , 那就分享给大家,没兴...

张腾元_Ternence
2013/07/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

小白带你认识netty(二)之netty服务端启动(上)

上一章 中的标准netty启动代码中,ServerBootstrap到底是如何启动的呢?这一章我们来瞅下。 server.group(bossGroup, workGroup);server.channel(NioServerSocketChannel.class).optio...

天空小小
33分钟前
1
0
聊聊storm trident batch的分流与聚合

序 本文主要研究一下storm trident batch的分流与聚合 实例 TridentTopology topology = new TridentTopology(); topology.newStream("spout1", spout) .p......

go4it
昨天
3
0
3分钟总结Mybatis别名

1.系统内置别名: 把类型全小写(resultType/paramType) 2.给某个类起别名 2.1 alias=”自定义” <typeAliases> <typeAlias type="com.bjsxt.pojo.People" alias="peo"/> </typeAli......

KingFightingAn
昨天
2
0
JAVA设计模式之模板方法模式和建造者模式

一、前期回顾 上一篇《Java 设计模式之工厂方法模式与抽象工厂模式》介绍了三种工厂模式,分别是工厂方法模式,简单工厂方法模式,抽象工厂模式,文中详细根据实际场景介绍了三种模式的定义,...

木木匠
昨天
8
0
C中的宏的使用(宏嵌套/宏展开/可变参数宏)

基本原则: 在展开当前宏函数时,如果形参有#或##则不进行宏参数的展开,否则先展开宏参数,再展开当前宏。 #是在定义两边加上双引号 #define _TOSTR(s) #sprintf(_TOSTR(test ABC))pr...

SamXIAO
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部