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層都是類似的,無論是按鍵分發流程還是觸控事件分發流程