Android Framework

Android事件传递

2021-08-16  本文已影响0人  Archer_J

驱动

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

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

-> 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

上一篇下一篇

猜你喜欢

热点阅读