Android事件传递

作者: Archer_J | 来源:发表于2021-08-16 15:07 被阅读0次

    驱动

    -> 触摸屏幕出发硬件驱动

    -> 产生原生态的内核事件

    -> Linux内核讲事件包装为通用的Event存到/dev/input/event[x]目录下

    SystemServer

    当系统启动时,SystemServer进程会启动一些列的系统服务,如AMS,WMS等,包括InputManagerService。

    InputManagerService

    作用:与硬件通信,接收屏幕输入事件。
    内部启动了一个InputReader读线程,从到/dev/input/目录拿到事件
    InputReader将事件分发给InputDispatcher线程,进行统一的事件分发调度
    IMS通过InputChannel进程间通讯(socket)将事件传递给应用进程
    InputChannel在ViewRootImpl.setView时,进行了注册
    当应用进程接收到消息,通过native回调到InputEventReceiver.dispachInputEvent -> ViewRootImpl
    当时间分发完成后,会调用finishInputEvent,告知InputDispatcher将事件移除,完成本次事件分发

    InputEventReceiver

    
    // 由native调用
    
    private void dispatchInputEvent(int seq, InputEvent event) {
    
        mSeqMap.put(event.getSequenceNumber(), seq);
    
        // 这里ViewRootImpl中注册了一个WindowInputEventReceiver
    
        onInputEvent(event);
    
    }
    
    

    ViewRootImpl

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
                int userId) {
                
           mView = view;
           
           // 屏幕输入事件
           mInputChannel = new InputChannel();
           ...
           
           requestLayout();
           
           // 添加window时,注册mInputChannel
           mWindowSession.addToDisplayAsUser(mWindow, ...., mInputChannel, ...);
           
           // 注册输入输入事件回调
           mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
                                Looper.myLooper());
           ...
           
           view.assignParent(this);
           
           ...
    }
    
    // 最终调用到ViewPostImeInputStage 中的 mView.dispatchTouchEvent(event)
    // 这里的mView为DecorView
    // 责任链模式,最终调用到ViewPostImeInputStage 中的 mView.dispatchPointerEvent
    // 内部调用了 mView.dispatchTouchEvent(event)
    // 这里的mView为DecorView
    private void deliverInputEvent(QueuedInputEvent q) {
        InputStage stage;
        ....
        // stage赋值操作
        ....
        if (stage != null) {
            stage.deliver(q);
        } else {
            finishInputEvent(q);
        }
    }
    
    final class WindowInputEventReceiver extends InputEventReceiver {
    
        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
        }
    
        @Override
        public void onInputEvent(InputEvent event) {
            ....
            // 这个方法最终调用到deliverInputEvent
            enqueueInputEvent(event, this, 0, true);
        }
    }
    
    
    
    // 责任链模式
    abstract class InputStage {
        private final InputStage mNext;
    
        public InputStage(InputStage next) {
            mNext = next;
        }
    
        public final void deliver(QueuedInputEvent q) {
            if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
                forward(q);
            } else if (shouldDropInputEvent(q)) {
                finish(q, false);
            } else {
                traceEvent(q, Trace.TRACE_TAG_VIEW);
                final int result;
                try {
                    result = onProcess(q);
                } finally {
                    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
                }
                apply(q, result);
            }
        }
    }
    
    

    DecorView

    
    // 调用到activity的dispatchTouchEvent
    
    @Override
    
    public boolean dispatchTouchEvent(MotionEvent ev) {
    
      final Window.Callback cb = mWindow.getCallback();
    
      // 这里的mWindow为PhoneWindow,cb为Activity
    
      return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
    
                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
    
    }
    
    // 调用父类即使ViewGroup的dispatchTouchEvent往下分发事件
    
    public boolean superDispatchTouchEvent(MotionEvent event) {
    
        return super.dispatchTouchEvent(event);
    
    }
    
    

    Activity

    
    public boolean dispatchTouchEvent(MotionEvent ev) {
    
      ...
    
      // 调用PhoneWindow的superDispatchTouchEvent
    
      if (getWindow().superDispatchTouchEvent(ev)) {
    
          return true;
    
      }
    
      return onTouchEvent(ev);
    
    }
    
    

    PhoneWindow

    
    @Override
    
    public boolean superDispatchTouchEvent(MotionEvent event) {
    
      // 调用DecorView
    
      return mDecor.superDispatchTouchEvent(event);
    
    }
    
    

    ViewGroup

    
    // 第一个触摸目标,用于判断是否有childView消费事件,链表结构
    
    private TouchTarget mFirstTouchTarget;
    
    @Override
    
    public boolean dispatchTouchEvent(MotionEvent ev) {
    
        final int action = ev.getAction();
        final int actionMasked = action & MotionEvent.ACTION_MASK;
    
            // 是否需要拦截
        final boolean intercepted;
        // ACTION_DOWN 或者 mFirstTouchTarget不为空,判断一下是否需要拦截
        if (actionMasked == MotionEvent.ACTION_DOWN
                        || mFirstTouchTarget != null) {
           // 判断是否允许拦截,childView通过调用ParentView.requestDisallowInterceptTouchEvent设置      
           final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
           // 允许拦截
           if (!disallowIntercept) {
                // 调用自身的onInterceptTouchEvent判断是否需要拦截
              intercepted = onInterceptTouchEvent(ev);
              ...
           } else {
                // 不允许拦截
              intercepted = false;
           }
         } else {
            // 如果mFirstTouchTarget为null且不是ACTION_DOWN,
            intercepted = true;
         }
            
         final boolean canceled = ...;
         TouchTarget newTouchTarget = null;
         boolean alreadyDispatchedToNewTouchTarget = false;
         // 不拦截
         if (!canceled && !intercepted) {
                ...
            // Down事件会判断一下是否有子View产生消费,初始化mFirstTouchTarget
                if (actionMasked == MotionEvent.ACTION_DOWN
               || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
               || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
               ...
               for (int i = childrenCount - 1; i >= 0; i--) {
                     final View child = ...
                     if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                      // 添加mFirstTouchTarget链表中
                        newTouchTarget = addTouchTarget(child, idBitsToAssign)
                      alreadyDispatchedToNewTouchTarget = true;
                      break;
                   }
               }
            }
         }
         
         // 没有相应的触摸目标
         if (mFirstTouchTarget == null) {
            // 当作普通的view进行分发
            handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
         } else {
                // 分发到触摸目标(整个链表)
                TouchTarget target = mFirstTouchTarget;
            while (target != null) {
                        final TouchTarget next = target.next;
                  // 如果已经分发过,则无需再次分发,排除DOWN事件已经分发过的事件
                  if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                     handled = true;
                  } else {
                            final boolean cancelChild = intercepted || ...;
                    // 向整个触摸目标链表分发事件,如果ViewGroup拦截了事件,则分发取消事件
                    if (dispatchTransformedTouchEvent(ev, cancelChild,
                            target.child, target.pointerIdBits)) {
                       handled = true;
                    }
                    // 如果ViewGroup拦截了事件,当遍历完触摸目标后,mFirstTouchTarget为null
                    if (cancelChild) {
                         ...
                       mFirstTouchTarget = next;
                       continue;
                    }
                  }
                  ...
                  target = next;
            }
         }
             return handled;
    
    }
    
    // childView事件分发
    
    // 将运动事件转换为特定子视图的坐标空间,过滤掉不相关的指针 ID,并在必要时覆盖其操作。 如果 child 为 null,则 MotionEvent 将改为发送到此 ViewGroup。
    
    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
    
                View child, int desiredPointerIdBits) {
    
        final boolean handled;
    
        // 分发取消的事件
    
        final int oldAction = event.getAction();
    
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
    
          event.setAction(MotionEvent.ACTION_CANCEL);
    
          if (child == null) {
    
              handled = super.dispatchTouchEvent(event);
    
          } else {
    
              handled = child.dispatchTouchEvent(event);
    
          }
    
          event.setAction(oldAction);
    
          return handled;
    
        }
    
        // 过滤不相关的指针
    
        // 如果由于某种原因我们最终处于不一致的状态,看起来我们可能会产生一个没有指针的运动事件,然后删除该事件。
    
        if (newPointerIdBits == 0) {
    
          return false;
    
        }
    
        ...
    
        // 一些变换操作
    
        final MotionEvent transformedEvent = ...
    
        if (child == null) {
    
          // 没有子View消费事件,则将事件传递给父类,其中会调用到onTouchEvent
    
          handled = super.dispatchTouchEvent(transformedEvent);
    
        } else {
    
          final float offsetX = mScrollX - child.mLeft;
    
          final float offsetY = mScrollY - child.mTop;
    
          transformedEvent.offsetLocation(offsetX, offsetY);
    
          handled = child.dispatchTouchEvent(transformedEvent);
    
        }
    
        return handled;
    
    }
    
    // 添加mFirstTouchTarget
    
    private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
    
        final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
    
        target.next = mFirstTouchTarget;
    
        mFirstTouchTarget = target;
    
        return target;
    
    }
    
    

    View

    
    // View响应事件,并传递到onTouchEvent方法中
    
    public boolean dispatchTouchEvent(MotionEvent event) {
    
    boolean result = false;
    
      ...
    
      final int actionMasked = event.getActionMasked();
    
      if (actionMasked == MotionEvent.ACTION_DOWN) {
    
          stopNestedScroll();
    
      }
    
      ... 
    
      ListenerInfo li = mListenerInfo;
    
      // View的OnTouchListener监听,先于OnClickListener,如果返回true,则OnClickListener不会响应
    
      if (li != null && li.mOnTouchListener != null
    
              && (mViewFlags & ENABLED_MASK) == ENABLED
    
              && li.mOnTouchListener.onTouch(this, event)) {
    
          result = true;
    
      }
    
      // View的onTouchEvent处理,及Click监听
    
      if (!result && onTouchEvent(event)) {
    
          result = true;
    
      }
    
      return result;
    
    }
    
    

    Click

    
    // View的onTouchEvent处理,及Click监听
    
    public boolean onTouchEvent(MotionEvent event) {
    
    final float x = event.getX();
    
      final float y = event.getY();
    
      final int viewFlags = mViewFlags;
    
      final int action = event.getAction();
    
      final boolean clickable = ...;
    
    
    
      // Disabled状态
    
    if ((viewFlags & ENABLED_MASK) == DISABLED) {
    
          ...
    
          return clickable;
    
      }
    
      if (clickable){
    
        switch (action) {
    
            case MotionEvent.ACTION_UP:
    
            ...
    
              // 点击处理及回调
    
            performClickInternal();
    
              break;
    
            ...
    
          }
    
          return true;
    
      }
    
    return false;
    
    
    
    }
    
    

    一个事件完整流程

    -> 驱动
    -> IMS
    -> InputEventReceiver
    -> ViewRootImpl
    -> DecorView(dispatchInputEvent)
    -> Activity(dispatchInputEvent)
    -> PhoneWindow(superDispatchTouchEvent)
    -> DecorView(父类ViewGroup的dispatchInputEvent)
    -> 分发给子View
    -> 回到ViewRootImpl,结束事件分发

    文字描述:
    1.驱动响应事件并包装为Event
    2.通过InputManagerService将Event传递到应用进程
    3.通过native回调到InputEventReceiver
    4.ViewRootImpl注册了InputChannel及InputEventReceiver,所以可以接收到事件,
    收到事件后,传递给DecorView.dispatchInputEvent,这里使用到了责任链模式
    5.DecorView调用Activity.dispatchInputEvent
    6.Activity调用PhoneWindow.superDispatchTouchEvent
    7.PhoneWindow调用DecorView.superDispatchTouchEvent
    8.DecorView调用父类super.dispatchInputEvent
    9.当没有子View消费,会调用Activity.onTouchEvent()
    10.不管有没有子View消费,都会回到ViewRootImp,然后将事件移除,结束这次事件分发

    示例

    RecyclerView的手势Down、Move、Up的分发,item的点击事件分发逻辑

    Down

    1.RecyclerView收到Down事件,由于子View没有请求拦截事件(disallowIntercept为false),所以会判断是否由自身拦截事件(onInterceptTouchEvent)

    2.由于Down事件没有触发RecyclerView的滚动,所以没有拦截(onInterceptTouchEvent返回false),事件会继续往下分发到子View(dispatchTouchEvent)

    3.子View的dispatchTouchEvent会将事件流转到onTouchEvent,由于子View设置了点击事件,所以onTouchEvent返回true表示进行消费

    4.RecyclerView的Down事件分发,有子View产生消费,所以mFirstTouchTarget不为null

    Move

    1.RecyclerView收到Move事件,由于mFirstTouchTarget不为null,所以会继续判断是否由自身拦截事件(onInterceptTouchEvent)

    2.如果onInterceptTouchEvent判断产生了滚动,则返回true表示要进行拦截,会向mFirstTouchTarget分发一个Cancel事件后,将mFirstTouchTarget置为null

    3.由于mFirstTouchTarget为null,所以后续的Move事件都会被RecyclerView拦截掉,不会再往下分发,会将事件分发给自身的onTouchEvent

    Up

    1.如果在Move中,RecyclerView产生滚动拦截了事件,则后续Move、Up都不会传递到子View

    2.RecyclerView收到Up事件,并且未产生滚动事件,会将事件继续分发到子View(同Down),

    3.子View收到Up事件后,onTouchEvent判断产生了点击事件后,回调OnClick

    相关文章

      网友评论

        本文标题:Android事件传递

        本文链接:https://www.haomeiwen.com/subject/ivwmbltx.html