Android事件传递

  源码解析

Posted by     Mrcodesniper on November 26, 2017

以native为终点的源码分析

Android中点击事件的来源 - 如何从硬件->Activity

先来看下这张图

引导

事件首先产生是在Activity,那么又是谁将事件传给activity

核心的代码是mWindow.setCallback(this); 是PhoneWindow传的 接着我们就继续深入

源头-ViewRootImpl

首先,点击事件是由用户的触摸行为所产生的,因此它必须要通过硬件来捕获,然后点击事件会交给WMS来处理

我们知道Activity启动以后,在它的onResume以后,DecorView才开始attach给WindowManager从而显示出来

接着,系统就会完成添加Window的过程 在这个过程中一个很重要的角色被创建了

root = new ViewRootImpl(view.getContext(), display);

在ViewRootImpl的setView方法中,会通过跨进程的方式向WMS(WindowManagerService)发起一个调用

在这个过程中,ViewRootImpl、DecorView和WMS会彼此向关联,从而将DecorView最终添加到Window上

同时会创建InputChannel、InputQueue和WindowInputEventReceiver来接受点击事件的消息(这些都在ViewRootImpl)

这些类中都有native的dispatchInputEvent接受底层传输的事件

InputEvent有2个子类:KeyEvent和MotionEvent,其中KeyEvent表示键盘事件,而MotionEvent表示触摸事件

最终会通过deliverInputEvent方法 ,事件会经过分发由对应的InputStage处理(stage.deliver)而触摸事件由ViewPostImeInputStage处理

经过processPointerEvent方法 会调用boolean handled = mView.dispatchPointerEvent(event); 这个mView是引用的decorview

中转站-Decorview

decorview本身没有这个方法 调用父类view的方法

public final boolean dispatchPointerEvent(MotionEvent event) {
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            return dispatchGenericMotionEvent(event);
        }
    }

判断是否是触摸事件或者是一般移动事件,若是触摸事件则执行dispatchTouchEvent(event) 再来看看decorview重写的这个方法

 终点-Activity

@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        final Window.Callback cb = mWindow.getCallback();
        return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
    }

这里mWindow.getCallback()就是我们之前mWindow.setCallback(this)设置的activity,然后执行cb.dispatchTouchEvent(ev) 这样触摸事件就传给了Activity

绕了一个大圈 触摸事件成功从wms获取到给Activity

Activity->ViewGroup

核心方法–dispatchTouchEvent–onTouchEvent

public boolean dispatchTouchEvent(MotionEvent ev) {
    // 如果是 down 事件,回调 onUserInteraction()
    // onUserInteraction 是空方法,可以用来判断用户是否正在和设备交互
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;//如果有view消费了 经过这里
        }
        return onTouchEvent(ev);//执行activity的onTouchEvent(ev)
    }
        
        

跟进getWindow().superDispatchTouchEvent(ev)进入PhoneWindow的superDispatchTouchEvent

return mDecor.superDispatchTouchEvent(event);进入decorview的superDispatchTouchEvent

(framelayout没有dispatchTouchEvent) 成功进入ViewGroup的dispatchTouchEvent

ViewGroup->View

进入ViewGroup的dispatchTouchEvent后

核心代码—onInterceptTouchEvent


public boolean dispatchTouchEvent(MotionEvent ev) {
    final boolean intercepted;
    if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {
    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    if (!disallowIntercept) {// disallowIntercept 代表着子View是否禁止让父ViewGroup拦截事件
        intercepted = onInterceptTouchEvent(ev);
        ev.setAction(action); // restore action in case it was changed
    } else {
        intercepted = false;
        }
    } 
    
    
                 if (newTouchTarget == null && childrenCount != 0) {
                        final float x = ev.getX(actionIndex);
                        final float y = ev.getY(actionIndex);
         
                        final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                        final boolean customOrder = preorderedList == null
                                && isChildrenDrawingOrderEnabled();
                        final View[] children = mChildren;
                        for (int i = childrenCount - 1; i >= 0; i--) {//循环子view 
                            final int childIndex = getAndVerifyPreorderedIndex(
                                    childrenCount, i, customOrder);
                            final View child = getAndVerifyPreorderedView(
                                    preorderedList, children, childIndex);

                            if (childWithAccessibilityFocus != null) {
                                if (childWithAccessibilityFocus != child) {
                                    continue;
                                }
                                childWithAccessibilityFocus = null;
                                i = childrenCount - 1;
                            }

                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }

                            newTouchTarget = getTouchTarget(child);
                            if (newTouchTarget != null) {
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }

                            resetCancelNextUpFlag(child);
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {//重点
                                // 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);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }

                            ev.setTargetAccessibilityFocus(false);
                        }
                        if (preorderedList != null) preorderedList.clear();
                    }

                    if (newTouchTarget == null && mFirstTouchTarget != null) {
                        // Did not find a child to receive the event.
                        // Assign the pointer to the least recently added target.
                        newTouchTarget = mFirstTouchTarget;
                        while (newTouchTarget.next != null) {
                            newTouchTarget = newTouchTarget.next;
                        }
                        newTouchTarget.pointerIdBits |= idBitsToAssign;
                    }
                }
            }
    
    
    
    
    if (mFirstTouchTarget == null) {
            // No touch targets so treat this as an ordinary view.
            // 交给自己处理,调用 ViewGroup 的 super.dispatchTouchEvent 方法
            handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    TouchTarget.ALL_POINTER_IDS);
        } 
    
}

循环子view 会根据点击坐标是否落在子view范围内以及子view是否正在动画来判断是否接收事件

如果找到了一个子 View 可以接收事件,那么就会调用它的 dispatchTouchEvent 方法。若 dispatchTouchEvent 方法返回 true 的话,说明该 View 确认处理该事件了 ,否则就继续遍历重复之前的流程了。

如果 mFirstTouchTarget 为空,那么有可能没有子 View 或者所有的子 View 都不处理该事件了 那么只能交给 ViewGroup 自己处理了,之后调用了 dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS) 方法

比较下:

子view的方法 dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign) ViewGroup的方法 dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS)

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;

        // Canceling motions is a special case.  We don't need to perform any transformations
        // or filtering.  The important part is the action, not the contents.
        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;
        }

很清楚如果子view为null的话执行super.dispatchTouchEvent(event); 由父类执行

如果有子view的话child.dispatchTouchEvent(event); 由子类执行

这里成功把触摸事件从ViewGroup传到子View了

View

进入源码

第一部分:

            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

这段代码 如果li.mOnTouchListener没有设置了触摸监听器就不会执行后面的li.mOnTouchListener.onTouch(代码顺序问题)

先执行了mOnTouchListener.onTouch

如果设置了OnTouchListener监听器就不会执行onTouchEvent 反之

假定为false 执行了onTouchEvent

子view可以重写onTouchEvent获取触摸事件

如果不实现会由默认实现

进入源码

第二部分:

//判断该view是否是可点击的
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
                
                
                
                case MotionEvent.ACTION_UP:
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    if ((viewFlags & TOOLTIP) == TOOLTIP) {
                        handleTooltipUp();
                    }
                    if (!clickable) {
                        removeTapCallback();
                        removeLongPressCallback();
                        mInContextButtonPress = false;
                        mHasPerformedLongPress = false;
                        mIgnoreNextUpEvent = false;
                        break;
                    }
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        // take focus if we don't have it already and we should in
                        // touch mode.
                        boolean focusTaken = false;
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                            focusTaken = requestFocus();
                        }

                        if (prepressed) {
                            setPressed(true, x, y);
                        }

                        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                            removeLongPressCallback();

                            if (!focusTaken) {
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    performClick();
                                }
                            }
                        }

                        if (mUnsetPressedState == null) {
                            mUnsetPressedState = new UnsetPressedState();
                        }

                        if (prepressed) {
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if (!post(mUnsetPressedState)) {
                            // If the post failed, unpress right now
                            mUnsetPressedState.run();
                        }

                        removeTapCallback();
                    }
                    mIgnoreNextUpEvent = false;
                    break;                

重点在ACTION_UP的 performClick();


public boolean performClick() {
    final boolean result;
    final ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnClickListener != null) {
        playSoundEffect(SoundEffectConstants.CLICK);
        li.mOnClickListener.onClick(this);
        result = true;
    } else {
        result = false;
    }

    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
    return result;
}

li.mOnClickListener != null判断是否设置了监听器

设置了的话执行li.mOnClickListener.onClick(this); 长按同理

执行了我们平常用的xxx.setOnClickListener(listener);

总结

总结

ps:activity没有拦截函数

reference