事件分為按鍵事件分發(fā),觸摸事件分發(fā),還有軌跡球事件,軌跡球已經(jīng)被淘汰,按鍵事件分發(fā)主要是在TV上,使用遙控器做按鍵操作。觸摸事件分發(fā)及pointerEvent則是觸摸屏設(shè)備的觸摸點(diǎn)分發(fā),此處主要討論keyEvent事件分發(fā)。
分發(fā)過程:
第一步,WindowManagerService(WMS) 中有一個(gè)KeyInputQueue的子類,該類內(nèi)部有一個(gè)thread去調(diào)用native方法讀取用戶的按鍵,觸摸消息,然后把消息保存到QueueEvent的消息隊(duì)列中,然后WMS中有一個(gè)對(duì)應(yīng)的hander來處理這個(gè)輸入消息隊(duì)列InputDispatcherThread,它內(nèi)部有一個(gè)線程對(duì)這些消息就行處理,然后分發(fā)給對(duì)應(yīng)的窗口(至于怎么匹配對(duì)應(yīng)的窗口這里不說)。
第二步:wms通過IPC的Binder機(jī)制把消息轉(zhuǎn)給對(duì)應(yīng)窗口PhoneWindow,它是actiivty的成員變量mWindow,PhoneWindow是一個(gè)window的子類,mWindow里面有一個(gè)成員變量mWindowManager,而mWindowManager是WindowMangerImpl類實(shí)例的引用,另外WindowMangerImpl里面包含ViewRoot,這個(gè)Viewroot就是Actvity與通信的一個(gè)handler對(duì)象,ViewRoot對(duì)應(yīng)一個(gè)ViewRootImpl,其實(shí)現(xiàn)了一個(gè)InputHandler,ViewRoot拿到消息后調(diào)用InputHandler去調(diào)用handleKey函數(shù),然后該函數(shù)再調(diào)用ViewRott的dispatchKey函數(shù),會(huì)發(fā)送一個(gè)DISPATCH_KEY消息,然后調(diào)用deliverKeyEvent函數(shù)然后分三步走:
1.dispatchKeyEventPreIme ?(分發(fā)給輸入法給一個(gè)機(jī)會(huì)去處理)
2.輸入法去響應(yīng),如果輸入法窗口處理了這個(gè)消息則直接返回,否則走第三步
3.deliverKeyEventPostIme(event,?sendDone);
下面是這三步的源碼:
1.privatevoiddeliverKeyEvent(KeyEvent?event,booleansendDone)?{
2.if(ViewDebug.DEBUG_LATENCY)?{
3.mInputEventDeliverTimeNanos?=?System.nanoTime();
4.}
5.
6.if(mInputEventConsistencyVerifier?!=null)?{
7.mInputEventConsistencyVerifier.onKeyEvent(event,0);
8.}
9.
10.//?If?there?is?no?view,?then?the?event?will?not?be?handled.
11.if(mView?==null||?!mAdded)?{
12.finishKeyEvent(event,?sendDone,false);
13.return;
14.}
15.
16.if(LOCAL_LOGV)?Log.v(TAG,"Dispatching?key?"+?event?+"?to?"+?mView);
17.
18.//?Perform?predispatching?before?the?IME.
19.if(mView.dispatchKeyEventPreIme(event))?{
20.finishKeyEvent(event,?sendDone,true);
21.return;
22.}
23.
24.//?Dispatch?to?the?IME?before?propagating?down?the?view?hierarchy.
25.//?The?IME?will?eventually?call?back?into?handleFinishedEvent.
26.if(mLastWasImTarget)?{
27.InputMethodManager?imm?=?InputMethodManager.peekInstance();
28.if(imm?!=null)?{
29.intseq?=?enqueuePendingEvent(event,?sendDone);
30.if(DEBUG_IMF)?Log.v(TAG,"Sending?key?event?to?IME:?seq="
31.+?seq?+"?event="+?event);
32.imm.dispatchKeyEvent(mView.getContext(),?seq,?event,?mInputMethodCallback);
33.return;
34.}
35.}
36.
37.//?Not?dispatching?to?IME,?continue?with?post?IME?actions.
38.deliverKeyEventPostIme(event,?sendDone);
39.}
第三步:我們不考慮輸入法窗口攔截key事件的操作,直接進(jìn)入第三步deliverKeyEventPostIme,從里面進(jìn)去有這么一段關(guān)鍵代碼
1.if(mView.dispatchKeyEvent(event))?{
2.finishKeyEvent(event,?sendDone,true);
3.return;
4.}
解析:mView是PhoneWindow.DecorView對(duì)象,DecorView是PhoneWindow的子類,繼承于FramwLayout,其實(shí)這個(gè)DecorView就是我們看到的activity上對(duì)應(yīng)的界面,它里面有一個(gè)title對(duì)應(yīng)狀態(tài)欄,還有一個(gè)contentview,對(duì)應(yīng)activity里面的setContentView方法。DecorView是定義在PhoneWindow里面,我們看這個(gè)內(nèi)部類的dispatchKeyEvent對(duì)應(yīng)的源碼:
public DecorView(Context context, int featureId) {
super(context);
mFeatureId = featureId;
}
public void setBackgroundFallback(int resId) {
mBackgroundFallback.setDrawable(resId != 0 ? getContext().getDrawable(resId) : null);
setWillNotDraw(getBackground() == null && !mBackgroundFallback.hasFallback());
}
@Override
public void onDraw(Canvas c) {
super.onDraw(c);
mBackgroundFallback.draw(mContentRoot, c, mContentParent);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
final int keyCode = event.getKeyCode();
final int action = event.getAction();
final boolean isDown = action == KeyEvent.ACTION_DOWN;
if (isDown && (event.getRepeatCount() == 0)) {
// First handle chording of panel key: if a panel key is held
// but not released, try to execute a shortcut in it.
if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) {
boolean handled = dispatchKeyShortcutEvent(event);
if (handled) {
return true;
}
}
// If a panel is open, perform a shortcut on it without the
// chorded panel key
if ((mPreparedPanel != null) && mPreparedPanel.isOpen) {
if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) {
return true;
}
}
}
//給activity一次處理dispatchKeyEvent的機(jī)會(huì),比如按下菜單鍵,沒消費(fèi)的話最后還是調(diào)用getWindow.superDispatchKeyEvent回調(diào)了DecorView中super.dispatch方法中去了,也就是ViewGroup的dispatch方法
if (!isDestroyed()) {
final Callback cb = getCallback();
final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event);
super.dispatchKeyEvent(event);
if (handled) {
return true;
}
return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
: PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
}
}
源碼注釋的很清楚,首先處理系統(tǒng)快捷鍵,然后調(diào)用Window.callback的dispatchKeyEvent()(cb.dispatchKeyEvent(event),cb是一個(gè)window.callback的接口實(shí)現(xiàn),這里就行接口回調(diào),實(shí)現(xiàn)window.Callback的主要是activity),所以就走到了Activity里面的dispatchKeyEvent方法里面去了,下面我們看看Activity里面改方法做了啥:
public boolean dispatchKeyEvent(KeyEvent event) {
onUserInteraction();
// Let action bars open menus in response to the menu key prioritized over
// the window handling it
if (event.getKeyCode() == KeyEvent.KEYCODE_MENU &&
mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
return true;
}
Window win = getWindow();
if (win.superDispatchKeyEvent(event)) {
return true;
}
View decor = mDecor;
if (decor == null) decor = win.getDecorView();
return event.dispatch(this, decor != null
? decor.getKeyDispatcherState() : null, this);
}
首先是win.superDispatchKeyEvent(event),PhoneWindow對(duì)應(yīng)的源碼是:
@Override
public boolean superDispatchKeyEvent(KeyEvent event) {
return mDecor.superDispatchKeyEvent(event);
}
這里還是調(diào)用的mDecor.superDispatchKeyEvent(event)對(duì)應(yīng)于DecorView的源碼是:
public boolean superDispatchKeyEvent(KeyEvent event) {
// Give priority to closing action modes if applicable.
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
final int action = event.getAction();
// Back cancels action modes first.
if (mActionMode != null) {
if (action == KeyEvent.ACTION_UP) {
mActionMode.finish();
}
return true;
}
}
return super.dispatchKeyEvent(event);
}
走到這里,我們發(fā)現(xiàn)它調(diào)用了super.dispatchKeyEvent(event),也就是FrameLayout,我追蹤到源碼并無重寫,故方法會(huì)走到ViewGroup的dispatchKeyEvent中去,這個(gè)是我們?nèi)粘=佑|的最多的了。至于ViewGroup的DispatchKeyEvent如果走,我們后面再說。我們回到mView.dispatchKeyEvent中去,我們回憶一下步驟的三步:a.處理系統(tǒng)快捷鍵dispatchKeyShortcutEvent(event);b.a沒有消費(fèi),則final ?cb.dispatchKeyEvent(event),此時(shí)進(jìn)入actiivty的dispatchKeyevent中去處理,首先處理menu操作,如果沒有消費(fèi),就調(diào)用phonwindow的superDispatchKeyEevent,最后走到的都是ViewGroup里面去遞歸, 如果我們看到的界面上的那些個(gè)view沒有消費(fèi)此事件,那么在在? ? ? ?return event.dispatch(this, decor != null
? decor.getKeyDispatcherState() : null, this);
這段代碼是actiivty里面dispatchKeyEvent最后的返回,傳的是actiivty自己的引用,跟蹤之后發(fā)現(xiàn)調(diào)用的是KeyEvent的OnkeyDown事件,也就是activity里面的onkeyDown.
綜上所述,activity里面的onkeydown是后于view里面的Onkeydown調(diào)用的。如果view里面的onkeydown消費(fèi)了此事件,那么activity的onKeyDown是走不到的。
最后來整理一下思路,理一下流程:
1.ViewRoot里面的InputHandler的handleKey,然后再是ViewRoot的dispatchKey,然后再是deliverKeyEvent,如果View系統(tǒng)有輸入法則被輸入法窗口攔截InputMethodManager對(duì)象的dispatchKeyEvent。攔截之前有一次DecorView對(duì)象mView的dispatchKeyEventPreIme(event)的操作。然后如果沒有攔截則RootViewImpl的deliverKeyEventPostIme方法。
源碼對(duì)應(yīng)于ViewRootImpl中:
1.privatevoiddeliverKeyEvent(QueuedInputEvent?q)?{
2.finalKeyEvent?event?=?(KeyEvent)q.mEvent;s
3.if(mInputEventConsistencyVerifier?!=null)?{
4.mInputEventConsistencyVerifier.onKeyEvent(event,0);
5.}
6.
7.if((q.mFlags?&?QueuedInputEvent.FLAG_DELIVER_POST_IME)?==0)?{
8.//?If?there?is?no?view,?then?the?event?will?not?be?handled.
9.if(mView?==null||?!mAdded)?{
10.finishInputEvent(q,false);
11.return;
12.}
13.
14.if(LOCAL_LOGV)?Log.v(TAG,"Dispatching?key?"+?event?+"?to?"+?mView);
15.
16.//?Perform?predispatching?before?the?IME.
17.if(mView.dispatchKeyEventPreIme(event))?{
18.finishInputEvent(q,true);
19.return;
20.}
21.
22.//?Dispatch?to?the?IME?before?propagating?down?the?view?hierarchy.
23.//?The?IME?will?eventually?call?back?into?handleImeFinishedEvent.
24.if(mLastWasImTarget)?{
25.InputMethodManager?imm?=?InputMethodManager.peekInstance();
26.if(imm?!=null)?{
27.finalintseq?=?event.getSequenceNumber();
28.if(DEBUG_IMF)?Log.v(TAG,"Sending?key?event?to?IME:?seq="
29.+?seq?+"?event="+?event);
30.imm.dispatchKeyEvent(mView.getContext(),?seq,?event,?mInputMethodCallback);
31.return;
32.}
33.}
34.}
35.
36.//?Not?dispatching?to?IME,?continue?with?post?IME?actions.
37.deliverKeyEventPostIme(q);
38.}
2.在ViewRootImpl的deliverKeyEventPostIme(q)方法中調(diào)用
1.//?Deliver?the?key?to?the?view?hierarchy.
2.if(mView.dispatchKeyEvent(event))?{
3.finishInputEvent(q,true);
4.return;
5.}
3.mView就是DecorView對(duì)象的dispatchKeyEvent操作做了三步,一個(gè)是處理快捷鍵,二個(gè)是調(diào)用Window.Callback對(duì)象的dispatchKeyEvent。
最后如果上面兩步都沒消費(fèi)就調(diào)用phonewindow的onKeyDown,onKeyUp事件。其中第二步是actiivty的dispatchKeyEvent為入口,里面是則會(huì)給增加了一個(gè)菜單按鈕的攔截,然后就又調(diào)用Window對(duì)象的superDispatchKeyEvent方法,其實(shí)就是PhoneWindow的方法攔截,這個(gè)方法最后還是調(diào)用的mDecorView.superDispatchKeyEvent方法,這個(gè)方法攔截了back按鍵事件之后又把事件轉(zhuǎn)給了DecorView的super.dispatchKeyEvent,最后走到了viewTree里面去了。