關於ANDROID事件-Android框架工程師的回覆

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

關於第一個, 其實相對獨立而且局部性比較強, 基本上與全局的事務處理不要緊. 但第二個是不少人容易陷入誤區的地方. 由於在整個Android事件處理的過程當中(按照他說的, 全在callback裏面也就是android的事件處理所有都是經過代碼調用進行的), 處處都是對事件處理結果(return value)的返回. 好比dispatch, 好比onintercept, 好比ontouchevent, 好比listeners中的返回. 因此有必要對這些概念進行一下梳理,否則不可能對它有個清晰的印象就更別提使用對它的瞭解來解決問題了! app

首先的基礎概念是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, 則都是經過它纔可能被調用的. less

這樣一來就清楚了, 即: 若是不考慮intercept即事件攔截的話, 全部的事件都遵循相同的途徑: Activity ->ViewGroup->View. 而且這個途徑指的是代碼調用的路徑, 不是指具體的執行路徑. 而代碼調用, 稍微研究過堆棧的都知道, 有兩個過程: 一個是棧的開劈, 一個是棧的回收. 這個跟Activity的堆疊方式有點象(這裏面其實包含我另外一個研究結果: Activity在很大程度上是Android系統中的一個很是優秀的內存節約設計, 由於它的引入給系統提供了使用中間結果進行操做而節省了深度堆棧調用所必需的deep Stack---試想每個應用都擁有一個長得不能再長, 而且不知道何時才能完全回收的堆棧. 這就是PC上的狀況. 在PC上面, 全部的應用都是一大陀混合交接在一塊兒的函數. 並且函數中保存的並不都是用戶的操做狀態而是函數本身的狀態----這些東西, 在PC上被視爲"工做"). ide

開闢與回收走兩條徹底相反的路, 這個跟事件處理的結果無關. 也就是說, 無論具體到每個View上的處理結果怎麼樣, 函數調用都是這麼工做的. 可是根據不一樣的返回值, 函數可能執行不一樣的行爲: 好比一個Viewgroup的葉子結點View返回了一個爲true的處理結果, 這個ViewGroup的dispatchTouchEvent方法就會認爲當前對於這個事件的處理已經結束因此提早返回而不是進一步去調用假想路徑(當全部的函數都返回false時此事件需通過的路徑)中的下一個節點:其自身的onTouchEvent. 而且這個邏輯也一樣的能夠應用在Activity上, 這一點也能夠從Activity的代碼(dispatchTouchEvent)中看出來. 函數

關於攔截, 是這麼理解的: 若是它的確攔截了, 那麼它就是攔截了. 具體的處理結果依賴於攔截對象自己的onTouchEvent方法的返回值, 若是它返回true, 那麼此攔截者自己的dispatchTouchEvent方法天然也就返回true, 因此致使其自己的調用者也認爲它已經完成了當前事件的處理因此向更上一級返回結果並維持這樣的處理方式直到Activity. 而且Activity在收到這樣的返回值後也將作一樣的動做: 即向上彙報--此事件已經被正確處理或者說被消費了. post

因而可知, 在創建正確的Android事件處理的幾個基本概念即對象之後, 惟一重要的兩樣東西即兩個: 一是假想的調用途徑; 一是值的返回. this

講到這裏要插一下Listener與onTouchEvent的關係. 注意這裏討論的二者都是Android事件系統中的核心元素而非衍生如ClickListener或GestureListener等東西, 由於這些東西都是要麼掛在原生Litener上, 要麼就掛在onTouchEvent上處理的東西因此討論它們是沒有必要的. 只要搞清楚了前二者的關係, 後面的就都清楚了. google

Listenr與onTouchEvent的關係其實很簡單, 在View類的dispatchTouchEvent方法中先調用前者後調用後者. 至於誰更重要這個問題其實也不存在. 重要的是要把它們倆象前面同樣也歸入整個事件處理的假想途徑就能夠了. 由於它們在代碼上的關係就是這樣的. 它們雖然是dispatchTouchEvent方法內部的代碼, 可是卻擁有與dispatchTouchEvent方法一樣的地位! 爲何這麼說呢? 你能夠把它們當作兩個分開執行的方法, 其結果與把它們放在一塊兒處理是同樣的. 徹底等價. 這就是緣由! spa

這是從事件處理鏈條的角度看的結果. 若是從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處理機制, 可是具體細節還不清楚.

相關文章
相關標籤/搜索