了解更多,移步Android觸摸事件傳遞機制系列詳解
在Android觸摸事件的傳遞(五)--輸入系統(tǒng)-InputChannel中事件傳遞給ViewRootImpl并調(diào)用deliverInputEvent處理事件。
1 ViewRootImpl的創(chuàng)建
- 我們可以猜測,如果外界想要傳遞點擊事件給
Activity,那么它就必須持有Activity的引用,這沒錯,在Activity的attach方法中:
mWindow持有了Activity的引用,它通過setCallback方法來持有Activity.(callback后面說)
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
attachBaseContext(context);
mFragments.attachHost(null );
mWindow = new PhoneWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
}
-
Activity啟動以后,在它的onResume以后,DecorView才開始attach給WindowManager從而顯示出來。(這句話好厲害) - 請看Activity的makeVisible方法,代碼如下:
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager()
wm.addView(mDecor, getWindow().getAttributes())
mWindowAdded = true
}
mDecor.setVisibility(View.VISIBLE)
}
- 系統(tǒng)就會完成添加
Window的過程,看WindowManagerGlobal的addView方法
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
ViewRootImpl root;
View panelParentView = null;
...這里省略了一堆代碼
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
- 可以看到,
ViewRootImpl創(chuàng)建了,在ViewRootImpl的setView方法(此方法運行在UI線程)中,會通過跨進程的方式向WMS(WindowManagerService)發(fā)起一個調(diào)用,從而將DecorView最終添加到Window上。 - 在這個過程中,
ViewRootImpl、DecorView和WMS會彼此向關(guān)聯(lián),同時會創(chuàng)建InputChannel、InputQueue和WindowInputEventReceiver來接受點擊事件的消息。
如何調(diào)用到deliverInputEvent移步Android觸摸事件的傳遞(五)--輸入系統(tǒng)-InputChannel
2 DecorView
ViewRootImpl的dispatchInputEvent方法調(diào)用deliverInputEvent方法來處理點擊事件的消息
private void deliverInputEvent(QueuedInputEvent q) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
q.mEvent.getSequenceNumber());
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
}
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
if (stage != null) {
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
- 在
ViewRootImpl中,有一系列類似于InputStage(輸入事件舞臺)的概念,每種InputStage可以處理一定的事件類型,比如AsyncInputStage、ViewPreImeInputStage、ViewPostImeInputStage等。 - 對于點擊事件來說,
ViewPostImeInputStage可以處理它,ViewPostImeInputStage中,有一個processPointerEvent方法,如下,它會調(diào)用mView的dispatchPointerEvent方法,注意,這里的mView其實就是DecorView。 - mView變量是在
setview方法中賦值的,對于應(yīng)用窗口來說, - mView變量指向
PhoneWindow的內(nèi)部類DecorView對象,但是調(diào)用的是父類View的dispatchPointerEvent方法,
注:繼承關(guān)系如下
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker{...}
public class FrameLayout extends ViewGroup {...}
public abstract class ViewGroup extends View implements ViewParent, ViewManager{..}
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
mAttachInfo.mUnbufferedDispatchRequested = false;
boolean handled = mView.dispatchPointerEvent(event);
if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
mUnbufferedInputDispatch = true;
if (mConsumeBatchedInputScheduled) {
scheduleConsumeBatchedInputImmediately();
}
}
return handled ? FINISH_HANDLED : FORWARD;
}
3 activity
在View的實現(xiàn)中,dispatchPointerEvent的邏輯如下,這樣一來,點擊事件就傳遞給了DecorView的父View的dispatchTouchEvent方法。
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
DecorView的dispatchTouchEvent的實現(xiàn)如下,需要強調(diào)的是,DecorView是PhoneWindow的內(nèi)部類
public boolean dispatchTouchEvent(MotionEvent ev) {
final Callback cb = getCallback();
return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
: super.dispatchTouchEvent(ev);
}
- 這里這個cb對象其實就是Activity,就這樣點擊事件就傳遞給了Activity或dialog。
-
activity和Dialog都是Callback接口的具體實現(xiàn),主要看activity的dispatchTouchEvent方法, - Activity實現(xiàn)了一個特殊的接口:Window.Callback。
致此觸摸事件傳遞給了activity。
4 Window.Callback
Window.Callback
/**
* API from a Window back to its caller. This allows the client to
* intercept key dispatching, panels and menus, etc.
*/
public interface Callback {
/**
* Called to process key events. At the very least your
* implementation must call
* {@link android.view.Window#superDispatchKeyEvent} to do the
* standard key processing.
*
* @param event The key event.
*
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchKeyEvent(KeyEvent event);
/**
* Called to process touch screen events. At the very least your
* implementation must call
* {@link android.view.Window#superDispatchTouchEvent} to do the
* standard touch screen processing.
*
* @param event The touch screen event.
*
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchTouchEvent(MotionEvent event);
/**
* Called to process trackball events. At the very least your
* implementation must call
* {@link android.view.Window#superDispatchTrackballEvent} to do the
* standard trackball processing.
*
* @param event The trackball event.
*
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchTrackballEvent(MotionEvent event);
...(省略若干代碼,下同)
Activity實現(xiàn)了一個特殊的接口:Window.Callback。
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback {
***
}
InputEvent有2個子類:KeyEvent和MotionEvent,其中KeyEvent表示鍵盤事件,而MotionEvent表示點擊事件。