Android中的事件分發機制

Android 是基於「事件驅動」模型的。所謂事件驅動,簡單地說就是你點什麼按鈕(即產生什麼事件),系統執行什麼操做(即調用什麼函數)。固然事件不只限於用戶的操做,事件驅動的核心天然是事件。java

事件的分類

從事件角度說,事件驅動程序的基本結構是由一個事件收集器、一個事件發送器和一個事件處理器組成。事件收集器專門負責收集全部事件,包括來自用戶的(如鼠標、鍵盤事件等)、來自硬件的(如時鐘事件等)和來自軟件的(如操做系統、應用程序自己等)。android

從廣義上來講,事件的發生源能夠分爲軟件硬件兩類,這裏側重對硬件部分的討論。c++

事件是由真實物理硬件產生的消息,代表設備使用者的某種意願。markdown

若是從硬件設備角度爲事件分類,最重要的有如下幾種分類app

  • 按鍵事件 (KeyEvent)socket

    由物理按鍵產生的事件。async

  • 觸摸事件 (TouchEvent)ide

    在觸摸屏上的點擊、滑動等事件,是 Android 系統中使用最普遍的事件。函數

  • 鼠標事件 (MouseEvent)oop

    鼠標操做引發的事件,如單擊、雙擊、移動等。

InputEvent

Android 針對上面提到的事件,提取出了一個統一的抽象接口,這就是 InputEvent

InputEvent 下面有兩個類:KeyEvent 和 MotionEvent。KeyEvent 用於表達按鍵事件,MotionEvent 則是將因此能產生 MoveEvent 的事件源進行統一管理。

事件的投遞流程

事件源產生後,Linux 內核收集到原始信息,Android 系統經過 /dev/input 下的節點來訪問當前的發生的事件並對 原始數據 進行提煉,而後交由 WMS 去分配,WMS 做爲 InputEvent 的派發者,將事件派發到指定的 window 去處理,當事件到達應用程序端的時候,就已經變成相對 可理解好處理 的了。

接下來咱們從 InputManagerService 入手,看看 InputEvent 事件是如何一步步變得好處理的。

InputManagerService 的建立

InputManagerService 是由 SystemServer 統一啓動

/*framework/base/services/java/com/android/server/SystemServer.java */

private void startOtherServices() {
   ...
   inputManager = new InputManagerService(context);
   wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
   new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
   ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
        DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
   ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
         /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
   inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
   inputManager.start();
複製代碼

從上面能夠看出,InputManagerService 的實例直接做爲 WindowManagerService 的參數傳入,另外, IMS 也將本身注入到了 ServiceManager 中。最後調用 inputManager.start()啓動 IMS。

/*framework/base/services/java/com/android/server/input/InputManagerService.java */

    public void start() {
         nativeStart(mPtr); // mPtr 是 IMS 在構造過程當中調用 nativeInit 所建立的 NativeInputManagerService 對象
         Watchdog.getInstance().addMonitor(this); // 將 IMS 加入監控體系

       /* 接下來是一些監聽事件處理,這裏的實現包含大量的 native 調用 */
    }
複製代碼

看來主要的事情都是 NativeInputManagerService 來處理的,接下來看看 NativeInputManagerService 的實現

/*framework/base/services/core/jni/com_android_server_input_InputManagerService.cpp*/

static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
   sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
   if (messageQueue == nullptr) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
    return 0;
   }

   NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
   messageQueue->getLooper());
   im->incStrong(0);
   return reinterpret_cast<jlong>(im);
}

// nativeInit 初始化了 NativeInputManager 對象

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
   NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

   status_t result = im->getInputManager()->start(); // getInputManager 返回的是 InputManager 對象
   if (result) {
      jniThrowRuntimeException(env, "Input manager could not be started.");
   }
}
複製代碼

nativeStart 最終實現的是 InputManager.start()方法, InputManager 爲 IMS 建立了2 個線程,分別是 InputReaderThreadInputDispatcherThread,其中InputReaderThread負責從從驅動節點讀取 Event,InputDispatcherThread專職分發。咱們重點關注 Dispatcher 的部分,對於 Reader 部分這裏不作細說。

/*framework/native/services/inputflinger/InputManager.cpp*/
InputManager::InputManager(
  const sp<InputReaderPolicyInterface>& readerPolicy,
  const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
  mDispatcher = new InputDispatcher(dispatcherPolicy);
  mClassifier = new InputClassifier(mDispatcher);
  mReader = createInputReader(readerPolicy, mClassifier); // mReader 與 mDispatcher 在這裏創建了關係,如此 Reader線程 就有了通道將事件告知 Dispatcher 線程去執行分發流程
  initialize();
}

void InputManager::initialize() {
  mReaderThread = new InputReaderThread(mReader);
  mDispatcherThread = new InputDispatcherThread(mDispatcher); //mDispatcherThread 的核心實現主要由 InputDispatcher 來實現
}

status_t InputManager::start() {
  status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
  if (result) {
     ALOGE("Could not start InputDispatcher thread due to error %d.", result);
     return result;
  }

  result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
  if (result) {
    ALOGE("Could not start InputReader thread due to error %d.", result);
    mDispatcherThread->requestExit();
    return result;
  }

  return OK;
}
複製代碼

InputDispatcher 的分發

接下來主要關注 InputDispatcher 的分發部分,InputDispatcher 將它獲知到的系統事件使用 EventEntry 結構表示,能夠分爲如下幾類

enum {
    TYPE_CONFIGURATION_CHANGED,
    TYPE_DEVICE_RESET,
    TYPE_KEY,
    TYPE_MOTION
};
複製代碼

本文討論的事件主要是這裏的 TYPE_KEY 類型;InputDispatcher 分發事件的核心函數爲InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime),其中針對 TYPE_KEY類型執行的函數爲done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime)

/*framework/native/services/inputflinger/InputDispatcher.cpp*/

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
   DropReason* dropReason, nsecs_t* nextWakeupTime) {
  ....
  省略了前面一些前期工做與攔截處理

  std::vector<InputTarget> inputTargets;

     int32_t injectionResult = ***findFocusedWindowTargetsLocked***(currentTime,
           entry, inputTargets, nextWakeupTime);   
     if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
       return false;
    }
    setInjectionResult(entry, injectionResult);
     if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
       return true;
    }
    // Add monitor channels from event's or focused display.
    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
    // 分發事件到目標中去
    dispatchEventLocked(currentTime, entry, inputTargets);

  return true;
 }
複製代碼

dispatchKeyLocked 先經過 findFocusedWindowTargetsLocked() 來尋找目標窗口,成功找到後會將其加入 inputTargets 中,而且將 injectionResult 賦值爲 INPUT_EVENT_INJECTION_SUCCEEDED ;最終經過dispatchEventLocked將事件分發給目標窗口。

findFocusedWindowTargetsLocked() 能拿到目標窗口,那這裏面確定少不了 WMS 的參與,由於 WMS 纔是真正 window 的管家。還記得最開始,IMS 與 WMS 綁定的那些代碼嗎?具體的講,WMS 內部 持有一個 InputManagerCallback 的回調 mInputManagerCallback,IMS 又經過 setWindowManagerCallbacks() 函數將 mInputManagerCallback 做爲入參傳入,這InputManagerCallback 定義了一系列與事件相關的接口,當 Window 變更的時候,WMS 都會經過 InputManagerCallback 來與 IMS 交流。更細點來講,WMS 到 InputDispatcher 的過程當中使用到了 InputMonitor 這個"中介",感興趣的能夠自行去研究。

dispatchEventLocked() 這個函數負責將事件通知給應用程序去處理的, InputDispatcher與 InputTarget 通訊是經過 InputChannel 來進行的, InputChannel 是經過 socket 來進行通訊的。具體流程是 WMS 在調用 addWindow()方法時候通會經過 InputChannel.openInputChannelPair(name)方法打開一個雙向通道,而這個openInputChannelPair (源碼路徑/* framework/native/libs/input/InputTransport.cpp */) 在 native 層的實現就是經過 socket 實現的。

經過上面的分析,咱們對 native 層的事件與 WMS 的傳遞大體有了瞭解,而咱們更爲關心的則是應用層的事件傳遞。咱們知道,Activity 調用addView()將 Window 添加到 WMS 的過程當中會使用到 ViewRootImpl 這個中介,咱們就從 ViewRootImpl 開始分析這一流程。

ViewRootImpl 中的事件傳遞

WMS 與 View 溝通的橋樑是經過 ViewRootImpl 來實現的。事件能回傳給 View 的前提是 View 所在的 Window 添加到了 WMS 中,而 View 添加到 WMS 的過程必然會走到 ViewRootImpl.setView() 方法,那麼就從這個方法開始跟蹤。

/*framework/base/core/java/android/view/ViewRootImpl.java*/

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {

...
if ((mWindowAttributes.inputFeatures
        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
    mInputChannel = new InputChannel();
}
 try {
     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                            mTempInsets);
 catch (RemoteException e) {
    mInputChannel = null;
 }finally {
 ...
 }
 
  if (mInputChannel != null) {
          if (mInputQueueCallback != null) {
                 mInputQueue = new InputQueue();
                 mInputQueueCallback.onInputQueueCreated(mInputQueue);
           }
           // WindowInputEventReceiver 繼承了 InputEventReceiver
           mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                  Looper.myLooper());
   }
                
  CharSequence counterSuffix = attrs.getTitle();
  mSyntheticInputStage = new SyntheticInputStage();
  InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
  InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
              "aq:native-post-ime:" + counterSuffix);
  InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
  InputStage imeStage = new ImeInputStage(earlyPostImeStage,
              "aq:ime:" + counterSuffix);
  InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
  InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
               "aq:native-pre-ime:" + counterSuffix);      
  mFirstInputStage = nativePreImeStage;
  mFirstPostImeInputStage = earlyPostImeStage;             
複製代碼

若是 Window 支持事件響應,會 new 一個 InputChannel 對象,而後經過 WindowSession.addToDisplay() 將其做爲參數傳入 WMS ,以後經過 InputEventReceiver 來監聽 InputChannel 的回傳數據。結合前面 dispatchEventLocked() 中提到的 InputChannel 充當的角色,不難理解,InputEventReceiver 最終能收到 native 層的回傳消息。

以後以責任鏈的形式封裝了 InputStage,默認狀況下責任鏈的起點是 NativePreImeInputStage,終點爲 SyntheticInputStage,(固然這不是絕對的,mSyntheticInputStage、mFirstInputStage、mFirstPostImeInputStage 這三個參數就是來肯定責任鏈真正的起點的)針對上層 View 的分發邏輯在 ViewPostImeInputStage 中。根據這點,不難猜想到 WindowInputEventReceiver 在處理分發的過程當中會將事件分發給這個 InputStage 的責任鏈去處理。實際流程也是如此。歸納來講就是,WindowInputEventReceiver 收到事件後,WindowInputEventReceiver.onInputEvent() 開始執行,它的職責是將已經消費的事件通知底層,將未消費的事件經過 enqueueInputEvent()加入隊,enqueueInputEvent()中的doProcessInputEvents()會不斷獲取當前須要處理的 InputEvent 而且將事件分發給 InputStage 去處理。當 Event 被 InputStage 處理完畢後,最終將處理結果經過InputEventReceiver.finishInputEvent()通知 native 層。

/*framework/base/core/java/android/view/ViewRootImpl.java*/
 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;
        }

        // 對 KeyEvent 的額外處理
        if (q.mEvent instanceof KeyEvent) {
            mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
        }

        if (stage != null) {
            handleWindowFocusChanged();
            stage.deliver(q); // 將事件分發給指定 InputStage 去處理
        } else {
            finishInputEvent(q);
        }
    }
    
    
    
         /** * Delivers an event to be processed. */
        public final void deliver(QueuedInputEvent q) {
            if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
                forward(q);
            } else if (shouldDropInputEvent(q)) {
                finish(q, false);
            } else {
                apply(q, onProcess(q)); // 這裏執行 InputStage 的 onProcess 方法
            }
        }
複製代碼

接下來重點關注ViewPostImeInputStage.onProcess()方法,對其餘 InputStage 感興趣的同窗能夠經過翻閱源碼自行閱讀,流程都差很少的。從 processGenericMotionEvent 中能夠看出,事件又被分發給了 View Tree 的根元素 mView ( 大部分狀況下是Activity )去處理,如此一來 變開啓了 View 的事件分發流程。

/*framework/base/core/java/android/view/ViewRootImpl$ViewPostImeInputStage.java*/
protected int onProcess(QueuedInputEvent q) {
    if (q.mEvent instanceof KeyEvent) {
        return processKeyEvent(q); //若是是 KeyEvent 走 KeyEvent 的處理流程
    } else {
        final int source = q.mEvent.getSource(); // 獲取事件源類型
        if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
            return processPointerEvent(q); // MotionEvent 類型的事件會執行到這來
        } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
            return processTrackballEvent(q); // 軌跡球事件類型
        } else {
            return processGenericMotionEvent(q); // Motion 事件
        }
    }
}

     private int processPointerEvent(QueuedInputEvent q) {
            final MotionEvent event = (MotionEvent)q.mEvent;

            // 將事件傳遞給 view 的 dispatchPointerEvent 處理
            boolean handled = mView.dispatchPointerEvent(event); 
            
            return handled ? FINISH_HANDLED : FORWARD;
        }
複製代碼

View 中的事件分發流程

View 中處理的事件主要爲 TouchEvent ,它能夠細分爲如下幾個重要類型:

  • ACTION_DOWN

    當手勢按下就會觸發,是後續一切事件的起點,通常會在這裏作一些狀態初始化工做。

  • ACTION_MOVE

    當手勢按下並拖動,就會產生 ACTION_MOVE 事件。

  • ACTION_UP

    ACTION_UP 是手勢操做的結束點。這裏經過與 ACTION_DOWN 時候設置的參數進行比較就能夠判斷是否爲長按事件等。

  • ACTION_CANCEL

    ACTION_CANCEL 事件不禁用戶主動產生,而是系統經過判斷後得出的結果。能夠簡單看作是手勢結束的標識,處理相似於 ACTION_UP,並作好清理工做。

View 中的 TouchEvent 投遞流程

/*framework/base/core/java/android/view/View.java */
public final boolean dispatchPointerEvent(MotionEvent event) {
    if (event.isTouchEvent()) {
        return dispatchTouchEvent(event);
    } else {
        return dispatchGenericMotionEvent(event);
    }
}


public boolean dispatchTouchEvent(MotionEvent event) {
        boolean result = false;
        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
        return result;
    }

複製代碼

View 中分發 TouchEvent 仍是比較簡單的,主要考慮到了兩個因素:

  • onTouch

    View 對象能夠經過 setOnTouchListener 來設置一個 Event 的監聽者,對應上面的 li.mOnTouchListener ,這樣當事件來臨時,View 會主動調用這個監聽者的 onTouch 方法去消費事件。

  • onTouchEvent

    用戶沒有指定 TouchListener ,或者 flag 指定爲 disable, 亦或是用戶指定了 TouchListener 可是 TouchListener.onTouch() 返回值爲 false 的狀況下,系統纔會將 event 傳遞給 onTouchEvent 。

ViewGroup 中的 TouchEvent 投遞流程

ViewGroup 的 TouchEvent 投遞流程相對複雜一些,由於涉及到對子對象的處理,多了一個攔截的概念。

@Override
 public boolean dispatchTouchEvent(MotionEvent ev) {
 boolean handled = false;
 if (onFilterTouchEventForSecurity(ev)) {
  final int action = ev.getAction();
  final int actionMasked = action & MotionEvent.ACTION_MASK;
  // Handle an initial down.
  if (actionMasked == MotionEvent.ACTION_DOWN) {
      // ACTION_DOWN 事件來臨,先清除掉以前的全部狀態
      cancelAndClearTouchTargets(ev);
      resetTouchState();
  }
  // 檢查攔截狀況
  final boolean intercepted;
  if (actionMasked == MotionEvent.ACTION_DOWN
          || mFirstTouchTarget != null) {
      final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
      if (!disallowIntercept) {
          // 若是disallowIntercept 爲 false,調用 onInterceptTouchEvent 方法
          intercepted = onInterceptTouchEvent(ev); 
          ev.setAction(action); // restore action in case it was changed
      } else {
          intercepted = false; //不攔截
      }
  } else {
      intercepted = true; //攔截
  }
  // 檢查 CANCEL 的狀況
  final boolean canceled = resetCancelNextUpFlag(this)
          || actionMasked == MotionEvent.ACTION_CANCEL;
  TouchTarget newTouchTarget = null;
  boolean alreadyDispatchedToNewTouchTarget = false;
  if (!canceled && !intercepted) {
      //不攔截的狀況
      if (actionMasked == MotionEvent.ACTION_DOWN
              || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
              || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {                       
          final int childrenCount = mChildrenCount;
          if (newTouchTarget == null && childrenCount != 0) {
              final float x = ev.getX(actionIndex);
              final float y = ev.getY(actionIndex);
              for (int i = childrenCount - 1; i >= 0; i--) {
                  // 省略了循環查找一個能處理此事件的 child 的過程
                  if (!child.canReceivePointerEvents()
                          || !isTransformedTouchPointInView(x, y, child, null)) {
                      ev.setTargetAccessibilityFocus(false);
                      continue;
                  }
                  if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                      // Child wants to receive touch within its bounds.
                       ...
                      break;
                  }
         ....
 return handled
}
複製代碼

ViewGroup 的 dispatchTouchEvent 首先會檢查是否被攔截,用intercepte來表示,若是 intercepted 爲 false,代表 ViewGroup 不但願攔截這一消息,於是它的子對象纔有機會來處理它。若是 intercepted 爲 true,說明 ViewGroup 須要攔截這個事件本身來處理,以後經過 dispatchTransformedTouchEvent()super.dispatchTouchEvent()觸發 onTouchEvent()回調。

intercepted 要爲 true 須要知足兩個條件:

  • disallowIntercept 爲 false

    能夠經過調用 ViewParent 接口的 requestDisallowInterceptTouchEvent(true) 使 disallowIntercept 爲false,Android 中僅 ViewGroup 和ViewRootImpl 繼承了 ViewParent 接口,而 ViewRootImpl 中的 requestDisallowInterceptTouchEvent 是一個空實現。

    也就是說,只有 ViewGroup.requestDisallowInterceptTouchEvent(true) 有效。

  • onInterceptTouchEvent() 返回值爲 true

    onInterceptTouchEvent() 能夠體現出每一個 ViewGroup 「差別化」的地方,這個方法是 Android 鼓勵你們在繼承 ViewGroup 時候重載的方法。

canReceivePointerEvents() 用於判斷當前 child 是否能接收 PointerEvents,dispatchTransformedTouchEvent() 則計算(x,y)這個點有沒有落在此 child 區域; 若是 child 不符合要求,直接跳過,能夠節省時間。若是找到了符合要求的 child,就經過 dispatchTransformedTouchEvent()來調用child.dispatchTouhEvent(),這個 child 有多是 View,也有多是 ViewGroup。

Activity 的事件分發

Activity 的 dispatchTouchEvent 首先會將事件交給它的 window 對象去處理,Activity 的 getWindow() 獲得的 Window 對象是 PhoneWindow,PhoneWindow 的 superDispatchTouchEvent() 方法又將事件分發給 mDecor 去處理,這個 mDecor 是一個 FrameLayout,是在 Activity 調用 setContentView() 時生成的。具體的流程這裏再也不展開,對這塊有疑問的同窗能夠自行跟蹤 setContentView() 的調用流程。FrameLayout 又是繼承 ViewGroup 的,如此便和上面 View 的 dispatchTouchEvent 創建了關聯。

/*framework/base/core/java/android/app/Activity.java*/
 public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
      onUserInteraction();
      }
    if (getWindow().superDispatchTouchEvent(ev)) {
      return true;
      }
    return onTouchEvent(ev);
 }
複製代碼
/*framework/base/core/java/com/android/internal/policy/PhoneWindow.java*/
  @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }
複製代碼
  • 每當 MotionEvent 事件分發到 Activity 都會調用到 onUserInteraction(),這是一個空實現,能夠利用這個方法作一些全局性的事件監聽。
  • 若是 getWindow().superDispatchTouchEvent(ev) 返回值爲 true,表示事件已經被分發到指定對象,在此過程當中事件將會傳遞到這個對象的 onTouchEvent 中去
  • 若是 getWindow().superDispatchTouchEvent(ev) 返回值爲 false,Activity 的 onTouchEvent 會被執行。
  • 經過重載 Activity 的 dispatchTouchEvent,並使其返回 false,根據 ViewRootImpl 流程分析中提到的內容,processPointerEvent 則會結束 ViewPostImeInputStage 的分發流程,轉到 SyntheticInputStage 去處理。也就是說 Activity 將再也不響應任何 MotionEvent 事件。
相關文章
相關標籤/搜索