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
[email protected]
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处理机制, 但是具体细节还不清楚.