1. 程式人生 > >Android原始碼解析(二十九)-->應用程式返回按鍵執行流程

Android原始碼解析(二十九)-->應用程式返回按鍵執行流程

從這篇文章中我們開始分析android系統的事件分發流程,其實網上已經有了很多關於android系統的事件分發流程的文章,奈何看了很多但是印象還不是很深,所以這裡總結一番。
android系統的事件分發流程分為很多部分:

  • Native層 –> ViewRootImpl層 –> DecorView層 –> Activity層 –> ViewGroup層 –> View層

所以android系統的事件分發流程是從Native層開始的,然後分發到ViewRootImpl中,然後分發到DecorView層,然後分發到ViewGroup層,最後分發到View層中。下面我們將從Native層開始分析事件的分發流程。

在Native層android系統的事件流程:

  • Android系統是從從底層驅動中獲取各種原始的使用者訊息,包括按鍵、觸控式螢幕、滑鼠、滾跡球等使用者事件訊息。

  • 在獲取使用者訊息之後,android系統會對最原始的訊息進行預處理,包括兩個方面:一方面,將訊息轉化成系統可以處理的訊息事件;另一方面,處理一些特殊的事件,比如HOME、MENU、POWER鍵等處理(前面的幾篇文章中我們已經分析了系統按鍵處理邏輯的執行流程)。

  • 將處理後的訊息事件分發到各個應用程序,這個需要使用IPC機制,Android系統使用管道來進行訊息的傳遞。

  • Android系統使用InputManager類來管理訊息,而具體的功能則是通過InputReaderThread和InputDispatcherThread兩個執行緒來實現。其中InputReaderThread執行緒負責訊息的讀取,而InputDispatcherThread則負責訊息的預處理和分發到各個應用程序中。

  • Acitivty系統在SystemServer程序中啟動WindowManagerService服務,然後在WindowManagerService服務中啟動InputManagerService服務。

可以看到在Native層,主要建立了兩個兩個執行緒,其中一個用於讀取訊息,另一個用於分發訊息,訊息經過分發最終會上傳至App中。

在ViewRootImpl層android系統的事件流程

在Native層的事件分發執行緒中,經過事件的分發流程,最終會呼叫InputEventSender的dispatchInputEventFinished方法,可以看一下具體程式碼的實現:

private
void dispatchInputEventFinished(int seq, boolean handled) { onInputEventFinished(seq, handled); }

在dispatchInputEventFinished方法中我們最終呼叫的是onInputEventFinished方法,然後我們檢視onInputEventFinished方法的實現,發現其是一個空方法。。。,好吧,經過分析我們發現,Native層最終呼叫的並不是InputEventSender,而是呼叫InputEventSender的子類ImeInputEventSender,即ImeInputEventSender的onInputEventFinished方法,該類定義在原始檔InputMethodManager中:

private final class ImeInputEventSender extends InputEventSender {
        public ImeInputEventSender(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
        }

        @Override
        public void onInputEventFinished(int seq, boolean handled) {
            finishedInputEvent(seq, handled, false);
        }
    }

可以看到在其onInputEventFinished方法中又呼叫了finishedInputEvent方法,這樣我們在繼續看一下finishedInputEvent方法的實現。

void finishedInputEvent(int seq, boolean handled, boolean timeout) {
        final PendingEvent p;
        synchronized (mH) {
            int index = mPendingEvents.indexOfKey(seq);
            if (index < 0) {
                return; // spurious, event already finished or timed out
            }

            p = mPendingEvents.valueAt(index);
            mPendingEvents.removeAt(index);
            Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER, mPendingEvents.size());

            if (timeout) {
                Log.w(TAG, "Timeout waiting for IME to handle input event after "
                        + INPUT_METHOD_NOT_RESPONDING_TIMEOUT + " ms: " + p.mInputMethodId);
            } else {
                mH.removeMessages(MSG_TIMEOUT_INPUT_EVENT, p);
            }
        }

        invokeFinishedInputEventCallback(p, handled);
    }

在方法finishedInputEvent中,經過一系列的處理之後最終呼叫的是invokeFinishedInputEventCallback方法,所以我們繼續看一下invokeFinishedInputEventCallback方法的實現。

void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
        p.mHandled = handled;
        if (p.mHandler.getLooper().isCurrentThread()) {
            // Already running on the callback handler thread so we can send the
            // callback immediately.
            p.run();
        } else {
            // Post the event to the callback handler thread.
            // In this case, the callback will be responsible for recycling the event.
            Message msg = Message.obtain(p.mHandler, p);
            msg.setAsynchronous(true);
            msg.sendToTarget();
        }
    }

可以發現這裡我們首先判斷PendingEvent的mHandler所在的執行緒是否是當前執行緒,若是的話則直接呼叫p.run方法,若不是的話則傳送一個非同步訊息,而非同步訊息最終也是執行的p.run方法,所以我們繼續看一下PendingEvent的run方法。

@Override
        public void run() {
            mCallback.onFinishedInputEvent(mToken, mHandled);

            synchronized (mH) {
                recyclePendingEventLocked(this);
            }
        }

可以發現在run方法中我們呼叫了mCallback的onFinishedInputEvent方法,需要說明的是這裡的mCallback就是我們ViewRootImpl中的ImeInputStage類物件,而這裡的ViewRootImpl物件就是我們的系統當前介面,前面我們分析Activity的載入繪製流程的時候知道Activity中儲存了一個Window物件用於表示視窗資訊,而Window物件內部就是通過ViewRootImpl物件實現視窗的載入繪製,所以這裡的mCallback物件就是我們當前的App獲取焦點的視窗的ViewRootImpl中的ImeInputStage物件,然後我們看一下該物件的onFinishedInputEvent方法的實現。

final class ImeInputStage extends AsyncInputStage
            implements InputMethodManager.FinishedInputEventCallback {
        ...
        @Override
        public void onFinishedInputEvent(Object token, boolean handled) {
            QueuedInputEvent q = (QueuedInputEvent)token;
            if (handled) {
                finish(q, true);
                return;
            }
            forward(q);
        }
    }

這樣經過一系列的呼叫之後我們訊息的處理邏輯上傳至了ViewRootImpl中,而在ViewRootImpl中經過一些列的呼叫之後我們ViewRootImpl$ViewPostImeInputStage.processKeyEvent方法:

at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:4152)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4114)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3662)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3715)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3681)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3807)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3689)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3864)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3662)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3715)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3681)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3689)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3662)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3715)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3681)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3840)
at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:4006)
at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:2272)
at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:1893)
at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:1884)
at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:2249)
at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:141)

這是通過異常資訊列印的堆疊資訊,從中我們可以看到在ViewRootImpl中我們經過一系列的呼叫之後最終執行的是:ViewRootImpl$ViewPostImeInputStage.processKeyEvent方法,這樣我們繼續看一下processKeyEvent方法。

private int processKeyEvent(QueuedInputEvent q) {
            ...
            // Deliver the key to the view hierarchy.
            if (mView.dispatchKeyEvent(event)) {
                return FINISH_HANDLED;
            }
            ...
}

可以看到這裡呼叫了mView的dispatchKeyEvent方法,而我們分析過Activity視窗載入繪製流程,從中我們知道ViewRootImpl中的mView物件就是我們PhoneWindow中的mDecorView物件(DecorView),所以經過層層呼叫我們最終執行到了DecorView層。

在DecorView層android系統的事件流程

從上面我們知道在ViewRootImpl中我們最終呼叫了mView.dispatchKeyEvent方法,即執行的是PhoneWindow%DecorView.dispatchKeyEvent方法。

@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;
                    }
                }
            }

            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);
        }

從中我們可以看到如果當前的PhoneWindow不是destroy莊則,則執行cb.dispatchKeyEvent方法,而這裡的callback物件就是我們的Activity物件,所以這裡最終會執行到Activity的dispatchKeyEvent方法。。。

在Activity層android系統的事件流程

所以我們這裡繼續看一下Actiivty中的dispatchKeyEvent方法:

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);
    }

從中我們可以看到我們首先呼叫了Activity的window物件的superDispatchKeyEvent方法,而這個方法就是將處理方法下發帶Activity中的View,而這裡我們分析的是返回按鍵,顯然的View層是無法處理這裡的返回按鍵的,所以win.superDispatchKeyEvent方法返回的是false,所以最終我們執行的是event.dispatch方法。這樣我們繼續看一下event.dispatch方法的實現。

public final boolean dispatch(Callback receiver, DispatcherState state,
            Object target) {
        switch (mAction) {
            ...
            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);
            ...
        }
        return false;
    }

這裡我們暫時分析一下ACTION_UP事件,可以發現這裡最終呼叫的是receiver.onKeyUp方法,而這裡的receiver就是我們的Actiivty,所以這裡又回到了Activity並且執行其onKeyUp方法。

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

看onKeyUp方法,我們可以發現當我們按的是返回按鍵時,其回調了onBackPressed方法,所以我們繼續看一下onBackPressed方法。

public void onBackPressed() {
        if (mActionBar != null && mActionBar.collapseActionView()) {
            return;
        }

        if (!mFragments.getFragmentManager().popBackStackImmediate()) {
            finishAfterTransition();
        }
    }

可以看到,在onBackPressed方法中,我們最終呼叫的是finishAfterTransition方法,所以繼續看一下這個方法的實現邏輯。

public void finishAfterTransition() {
        if (!mActivityTransitionState.startExitBackTransition(this)) {
            finish();
        }
    }

O(∩_∩)O哈哈~,原來finish方法是在這裡呼叫的,這樣我們按下返回按鍵並擡起之後,經過層層的呼叫之後最終呼叫了我們的finish方法,而這個方法就是finish掉Activity的方法,也就解釋了我們在App中預設按下返回按鍵之後Acitivty會被銷燬了。

總結:

  • 本文中由於是分析的返回按鍵的處理流程,所以事件的分發流程沒有做說明,下面的文章中會著重介紹Android的事件分發流程;

  • 事件分發流程從Native –> ViewRootImpl層 –> DecorView層 –> Activity層都是類似的,無論是按鍵分發流程還是觸控事件分發流程