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 事件。
相关文章
相关标签/搜索