Android touch事件传输过程

一、先看看整理流程图:


二、事件从Framework层如何传到UI进程

实际上就是实际从 WindowManagerService 传递到ViewRootImpl层。

我们在ViewRootImpl 可以看到这几个属性:


    InputChannel mInputChannel; //输入事件channel
    InputQueue.Callback mInputQueueCallback; //队列回掉
    InputQueue mInputQueue; //输入队列


我们在setView方法中, 将ViewRootImpl的inputChannel对象交给了WindowManagerService

  res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);

在WindowManagerService#addWindow方法中将channel与nativeChannel关联上

 public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
   ...
     final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
            if  (openInputChannels) {
                win.openInputChannel(outInputChannel);
            }

....


在看看win.openInputChannel方法

  void openInputChannel(InputChannel outInputChannel) {
        ....
           String name = getName();
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
        mInputChannel = inputChannels[0];
        mClientChannel = inputChannels[1];
        mInputWindowHandle.inputChannel = inputChannels[0];
        if (outInputChannel != null) {
            mClientChannel.transferTo(outInputChannel);
            mClientChannel.dispose();
            mClientChannel = null;
        } else {
            // If the window died visible, we setup a dummy input channel, so that taps
            // can still detected by input monitor channel, and we can relaunch the app.
            // Create dummy event receiver that simply reports all events as handled.
            mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
        }    
           //最终将输入channel和inputManager关联了。最终事件就传到mInputWindowHandle,
          // mInputWindowHandle 和 outInputChannel 是配对的,可以理解为mInputWindowHandle接收到事件后
        // 同时通知传递给了outInputChannel, 也就是 ViewRootImpl的InputChannel
        mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
    }




再Ui进程,ViewRootImpl#WindowInputEventReceiver

 mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }
final class WindowInputEventReceiver extends InputEventReceiver {
        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
        }
        @Override
        public void onInputEvent(InputEvent event) {
            enqueueInputEvent(event, this, 0, true); //得到input事件,并且放入队列
        }
...
    }
 public InputEventReceiver(InputChannel inputChannel, Looper looper) {
        if (inputChannel == null) {
            throw new IllegalArgumentException("inputChannel must not be null");
        }
        if (looper == null) {
            throw new IllegalArgumentException("looper must not be null");
        }
        mInputChannel = inputChannel;
        mMessageQueue = looper.getQueue();
        
        //native层不断从inputChannel中传递事件到MessageQueue中,得到从WindowManagerService中的事件
        mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                inputChannel, mMessageQueue);
        mCloseGuard.open("dispose");
    }
void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
        adjustInputEventForCompatibility(event);
  
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
        
        QueuedInputEvent last = mPendingInputEventTail;
        ...
           doProcessInputEvents(); //处理分发event。最终调用到了mView.dispatchPointerEvent
    }


三、UI层的传递

ViewRootImpl 内部类接收到来自WindowManagerService的事件之后。通过调用view.dispatchPointerEvent 向下分发事件。

View#dispatchPointerEvent
public final boolean dispatchPointerEvent(MotionEvent event) {
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event); 
        } else {
            return dispatchGenericMotionEvent(event); //这个是什么shi
        }
    }

ViewRootImpl 中的view其实就是DecorView。让我们看看DecorView的dispatchTouchEvent

 public boolean dispatchTouchEvent(MotionEvent ev) {
        final Window.Callback cb = mWindow.getCallback();  // 这里其实是activity#attach方法中设置,callback在activity中实现
        return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
    }

mWindow.getCallback()其实就是activity 中实现了这个接口。好的,让我们看看activity#dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction(); // 空实现,表示用户开始交互
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
          // 若getWindow().superDispatchTouchEvent(ev)的返回true
                // 则Activity.dispatchTouchEvent()就返回true,则方法结束。即 :该点击事件停止往下传递 & 事件传递过程结束
                // 否则:继续往下调用Activity.onTouchEvent
            return true;
        }
        return onTouchEvent(ev);
    }

让我们分析下getWindow().superDispatchTouchEvent。 其实就是调用的PhoneWindow#superDispatchTouchEvent

 public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event); //DecorView
    }
//DecorView#superDispatchTouchEvent
 public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
        // 调用父类的方法 = ViewGroup的dispatchTouchEvent()
        // 即 将事件传递到ViewGroup去处理,详细请看ViewGroup的事件分发机制
    }


好的,让我们继续往下看Activity#onTouchEvent

 // 当一个点击事件未被Activity下任何一个View接收 / 处理时
 public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }
        return false; // 返回,事件分发结束,
    }
//Window#shouldCloseOnTouch  按下事件,不再context边界范围内 就返回true
 public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
        if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
                && isOutOfBounds(context, event) && peekDecorView() != null) {
            return true;
        }
        return false;
    }

小结:以下是touchEvent传递到Activity#dispatchTouchEvent这里的流程图。

在流程图中,getWindow.superDispatchTouchEvent最终实现是在 ViewGroup#ViewGroup的dispatchTouchEvent。

好了,解下来分析ViewGroup#ViewGroup的dispatchTouchEvent

  @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        ....
        boolean handled = false;
      final boolean intercepted;
      if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) { //如果是down事件或者说,是down之后的事件比如up,需要判断是否拦截
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev); //判断是否拦截,若是button、或者在滚动条上 返回true
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
      } else {
                // There are no touch targets and this action is not an initial down
                // so this view group continues to intercept touches.
                intercepted = true; //如果不是DOWN事件,并且没有触摸目标。就拦截住
       }
      
            if (intercepted || mFirstTouchTarget != null) {
                ev.setTargetAccessibilityFocus(false);
            }
     如果没有被拦截,并且不是取消事件
        if (!canceled && !intercepted) {
          //,找到当前获取焦点的view
           View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
                        ? findChildWithAccessibilityFocus() : null;
          ....
            //先将ziView进行排序
             final ArrayList<View> preorderedList = buildTouchDispatchChildList()
            //依次判断子View是否处理当前事件
             for (int i = childrenCount - 1; i >= 0; i--) {
               ....
               if (childWithAccessibilityFocus != null) { //寻找当前焦点的子View,否则一直往下查找
                                if (childWithAccessibilityFocus != child) {
                                    continue;
                                }
                                childWithAccessibilityFocus = null;
                                i = childrenCount - 1;
                            }
                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) { //判断当前View是否能处理触摸点
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }
                            newTouchTarget = getTouchTarget(child);
                            if (newTouchTarget != null) {
                                // Child is already receiving touch within its bounds.
                                // Give it the new pointer in addition to the ones it is handling.
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }
                          if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { //如果有子View处理事件
                                // Child wants to receive touch within its bounds.
                                mLastTouchDownTime = ev.getDownTime();
                                if (preorderedList != null) {
                                    // childIndex points into presorted list, find original index
                                    for (int j = 0; j < childrenCount; j++) {
                                        if (children[childIndex] == mChildren[j]) {
                                            mLastTouchDownIndex = j;
                                            break;
                                        }
                                    }
                                } else {
                                    mLastTouchDownIndex = childIndex;
                                }
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                newTouchTarget = addTouchTarget(child, idBitsToAssign); //mFirstTouchTarget 设置为当前处理的View
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }...
             }
              ....
             // Dispatch to touch targets.
            if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
              //也就是说当intercept为true的时候,就当成是view使用,调用view的dispatch, 然后是ontouch。也即是在本层处理touch事件。
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } 
            ..
              
 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;
              if (child == null) {
                    handled = super.dispatchTouchEvent(event);  //调用View的dispatch,在本层被消耗掉
                } else {
                    final float offsetX = mScrollX - child.mLeft;
                    final float offsetY = mScrollY - child.mTop;
                    event.offsetLocation(offsetX, offsetY);
                    handled = child.dispatchTouchEvent(event); //调用子View的Dispatch,子view可能是View Group 还可以继续向下传递
                    event.offsetLocation(-offsetX, -offsetY);
                }
                return handled;
          return handled;
            }

小结下,此处ViewGroup#dispatchEvent处理事件流程


可以看到, 这里ViewGroup最终将事件传递给了View处理。

View#dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent event) {
        // If the event should be handled by accessibility focus first.
        if (event.isTargetAccessibilityFocus()) {
            // We don't have focus or no virtual descendant has it, do not handle the event.
            if (!isAccessibilityFocusedViewOrHost()) {
                return false;
            }
            // We have focus and got the event, then use normal event dispatch.
            event.setTargetAccessibilityFocus(false);
        }
  
   if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
          // 看,这里执行到OnTouch了
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
.....
 


  //View#onTouchEvent
    public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        final int viewFlags = mViewFlags;
        final int action = event.getAction();
        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
            }
            // A disabled view that is clickable still consumes the touch
            // events, it just doesn't respond to them.
            return (((viewFlags & CLICKABLE) == CLICKABLE
                    || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                    || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
        }
        if (mTouchDelegate != null) { // 设置了其他代理,代理响应处理
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
        }
        if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
                (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) { //如果能够满足能够被点击、被长按、CONTEXT_CLICKABLE 就处理,并返回true
           .... //处理各类事件
            }
            return true;
        }
        return false; //否则返回为false
    }
  • Android事件分发总是先传递到ViewGroup、再传递到View


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值