天天看點

Android Activity的按鍵事件處理流程

       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.
 *
 * @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);
}      

     我們看到,menu鍵會在分發到A所在的window前被actionbar優先處理, window分發/處理按鍵的函數是superDispatchKeyEvent(), 這個函數是個abstract函數,需要結合具體的window實作源碼來分析(Activity的window貌似是一個叫PhoneWindow的類)。 最關鍵的是要看event的dispatch()函數了,這個函數的第一個參數Receiver是Callback接口類型,  這裡傳入的是this,也即目前的Activity。 這個函數裡會根據Action的值來執行Activity的onKeyDown()/onKeyUp()/onKeyMultiple()函數。 KeyEvent.dispatch()函數的源碼如下:

/**
 * 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;
}