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) != 0) {
                    return processPointerEvent(q);
                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                    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 < 0 ? 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章