Android Activity的按鍵事件處理流程
阿新 • • 發佈:2019-01-26
Android裡,Activity按鍵事件相關的分發/處理函式有如下幾個:
1) public boolean dispatchKeyEvent(KeyEvent event);
2)public boolean onKeyDown(int keyCode, KeyEvent event);
3)public void onBackPressed(); //back鍵的處理函式
app開發者一般會override這幾個函式來做一些自定義處理,那麼這幾個函式的呼叫關係以及執行的先後順序是怎麼樣的呢? 今天抽時間看了一下Activity類的原始碼,總算理清了一些頭緒,趕緊寫下來記錄已經跟大家share一下~!
首先說明一下這3個函式的執行順序就是我上面列出來的順序。具體每個函式是如何呼叫到的,下面我們通過原始碼來做一下解釋和說明。假設使用者在某個Activity A上按了back鍵, 那麼肯定是A的dispatchKeyEvent()方法先被呼叫到, 這個函式的原始碼如下:
/** * Called to process key events. You can override this to intercept all * key events before they are dispatched to the window. Be sure to call * this implementation for key events that should be handled normally.我們看到,menu鍵會在分發到A所在的window前被actionbar優先處理, window分發/處理按鍵的函式是superDispatchKeyEvent(), 這個函式是個abstract函式,需要結合具體的window實現原始碼來分析(Activity的window貌似是一個叫PhoneWindow的類)。 最關鍵的是要看event的dispatch()函數了,這個函式的第一個引數Receiver是Callback介面型別, 這裡傳入的是this,也即當前的Activity。 這個函式裡會根據Action的值來執行Activity的onKeyDown()/onKeyUp()/onKeyMultiple()函式。 KeyEvent.dispatch()函式的原始碼如下:* * @param event The key event. * * @return boolean Return true if this event was consumed. */ 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); }
/** * Deliver this key event to a {@link Callback} interface. If this is * an ACTION_MULTIPLE event and it is not handled, then an attempt will * be made to deliver a single normal event. * * @param receiver The Callback that will be given the event. * @param state State information retained across events. * @param target The target of the dispatch, for use in tracking. * * @return The return value from the Callback method that was called. */ public final boolean dispatch(Callback receiver, DispatcherState state, Object target) { switch (mAction) { case ACTION_DOWN: { mFlags &= ~FLAG_START_TRACKING; if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state + ": " + this); boolean res = receiver.onKeyDown(mKeyCode, this); //呼叫Activity的onKeyDown() if (state != null) { if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) { if (DEBUG) Log.v(TAG, " Start tracking!"); state.startTracking(this, target); } else if (isLongPress() && state.isTracking(this)) { try { if (receiver.onKeyLongPress(mKeyCode, this)) { if (DEBUG) Log.v(TAG, " Clear from long press!"); state.performedLongPress(this); res = true; } } catch (AbstractMethodError e) { } } } return res; } case ACTION_UP: if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state + ": " + this); if (state != null) { state.handleUpEvent(this); } return receiver.onKeyUp(mKeyCode, this); //呼叫Activity的onKeyUp()函式 case ACTION_MULTIPLE: final int count = mRepeatCount; final int code = mKeyCode; if (receiver.onKeyMultiple(code, count, this)) { return true; } if (code != KeyEvent.KEYCODE_UNKNOWN) { mAction = ACTION_DOWN; mRepeatCount = 0; boolean handled = receiver.onKeyDown(code, this); if (handled) { mAction = ACTION_UP; receiver.onKeyUp(code, this); } mAction = ACTION_MULTIPLE; mRepeatCount = count; return handled; } return false; } return false; }
我們再來看一下Activity的onKeydown()函式裡都幹了些什麼? 對於back鍵來說, 在Android 2.0以前,會直接呼叫onBackPressed()函式;在Android 2.0之後,會呼叫event.startTracking(), 至於onBackPressed()函式,是在onKeyUp()裡呼叫的:
public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.ECLAIR) { event.startTracking(); } else { onBackPressed(); } return true; } 。。。。 }
public boolean onKeyUp(int keyCode, KeyEvent event) { if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.ECLAIR) { if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking() && !event.isCanceled()) { onBackPressed(); return true; } } return false; }