天天看點

ViewRootImpl事件分發筆記

  • 使用者點選螢幕産生觸摸行為,該觸摸行為由底層硬體捕獲并傳遞。

    硬體 -> ViewRootImpl -> DecorView -> PhoneWindow -> Activity

  • 在ViewRootImpl中準備了一條InputStage鍊。InputStage是個抽象類,有以下幾個實作類,并形成單向鍊:NativePreImeInputStage -> ViewPreImeInputStage -> ImeInputStage -> EarlyPostImeInputStage -> NativePostImeInputStage -> ViewPostImeInputStage -> SyntheticInputStage.
  • ViewRootImpl收到觸摸事件後經曆了如下方法:dispatchInputEvent() -> enqueueInputEvent() -> doProcessInputEvents() -> deliverInputEvent()。其中enqueueInputEvent()中生成了QueuedInputEvent,它封裝了InputEvent和InputEventReceiver。在deliverInputEvent()中把QueuedInputEvent交個InputStage鍊逐一處理并傳遞。InputStage處理事件的方法為onProcess,對于點選事件,ViewPostImeInputStage可以處理它。下面是ViewPostImeInputStage.onProcess:
@Override
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            } else {
                // If delivering a new non-key event, make sure the window is
                // now allowed to start updating.
                handleDispatchWindowAnimationStopped();
                final int source = q.mEvent.getSource();
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != ) {
                    return processPointerEvent(q);
                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != ) {
                    return processTrackballEvent(q);
                } else {
                    return processGenericMotionEvent(q);
                }
            }
        }
           

在ViewPostImeInputStage裡面有判斷是鍵盤事件還是觸摸事件,這裡我們隻看觸摸事件,調用到了processPointerEvent(q)方法

private int processPointerEvent(QueuedInputEvent q) {
        final MotionEvent event = (MotionEvent)q.mEvent;
        mAttachInfo.mUnbufferedDispatchRequested = false;
        boolean handled = mView.dispatchPointerEvent(event);
        ...
        return handled ? FINISH_HANDLED : FORWARD;
}
           

而這裡調用到了mView.dispatchPointerEvent,這裡的mView就是DecorView。

再看View的dispatchPointerEvent

public final boolean dispatchPointerEvent(MotionEvent event) {
       if (event.isTouchEvent()) {
           return dispatchTouchEvent(event);
       } else {
           return dispatchGenericMotionEvent(event);
       }
}
           

DecorView重寫了dispatchTouchEvent()方法:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
      final Callback cb = getCallback();
      return cb != null && !isDestroyed() && mFeatureId <  ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
           

可以看到,這裡調用了callback.dispatchTouchEvent,Callback是Window裡面的一個接口

public interface Callback {
       ...
       public boolean dispatchKeyEvent(KeyEvent event);
       ...
       public boolean dispatchTouchEvent(MotionEvent event);
}
           

而實作了Callback的正是Activity

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback {
        ..
}
           

而在Activity的attach方法中

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) {
        ...
        mWindow = new PhoneWindow(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
  }
           

而Activity收到觸摸事件後又回傳給了DecorView,下面是Activity的dispatchTouchEvent()

public boolean dispatchTouchEvent(MotionEvent ev) {
       if (ev.getAction() == MotionEvent.ACTION_DOWN) {
           onUserInteraction();
       }
       if (getWindow().superDispatchTouchEvent(ev)) {
           return true;
       }
       return onTouchEvent(ev);
}
           

PhoneWindow的superDispatchTouchEvent()如下:

@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
       return mDecor.superDispatchTouchEvent(event);
}
           

DecorView的superDispatchTouchEvent()如下:

public boolean superDispatchTouchEvent(MotionEvent event) {
       return super.dispatchTouchEvent(event);
}
           

接下來就是常見的事件分發機制了。

參考文章:

https://www.jianshu.com/p/9e6c54739217

繼續閱讀