Android中的事件分发机制

2,091 阅读12分钟

Android 是基于“事件驱动”模型的。所谓事件驱动,简单地说就是你点什么按钮(即产生什么事件),系统执行什么操作(即调用什么函数)。当然事件不仅限于用户的操作,事件驱动的核心自然是事件。

事件的分类

从事件角度说,事件驱动程序的基本结构是由一个事件收集器、一个事件发送器和一个事件处理器组成。事件收集器专门负责收集所有事件,包括来自用户的(如鼠标、键盘事件等)、来自硬件的(如时钟事件等)和来自软件的(如操作系统、应用程序本身等)。

从广义上来说,事件的发生源可以分为软件硬件两类,这里侧重对硬件部分的讨论。

事件是由真实物理硬件产生的消息,表明设备使用者的某种意愿。

如果从硬件设备角度为事件分类,最重要的有以下几种分类

  • 按键事件 (KeyEvent)

    由物理按键产生的事件。

  • 触摸事件 (TouchEvent)

    在触摸屏上的点击、滑动等事件,是 Android 系统中使用最广泛的事件。

  • 鼠标事件 (MouseEvent)

    鼠标操作引起的事件,如单击、双击、移动等。

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 事件。