Android11 事件分发流程

Android 11 输入系统之InputDispatcher和应用窗口建立联系一文中介绍到,当InputDispatcher写入数据后,客户端这边就会调用handleEvent方法接收数据

c 复制代码
//frameworks\base\core\jni\android_view_InputEventReceiver.cpp
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
   //省略

    if (events & ALOOPER_EVENT_INPUT) {//之前构造数据的时候,events为ALOOPER_EVENT_INPUT
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? 1 : 0;
    }
    
  //省略

继续调用consumeEvents处理

c 复制代码
//frameworks\base\core\jni\android_view_InputEventReceiver.cpp
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
	//省略
	for (;;) {
        uint32_t seq;
        InputEvent* inputEvent;

        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent);//1
		//省略
			case AINPUT_EVENT_TYPE_MOTION: {
        
                MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);//使用inputEvent构造MotionEvent对象
                if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
                    *outConsumedBatch = true;
                }
                inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);//创建java层的MotionEvent对象,并将该对象的mNativePtr指向c++的MotionEvent对象
                break;
            }

	//省略
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);//2
       
	//省略
}

注释1处接收InputDispatcher发过来的数据,并将数据封装成InputEvent对象,注释2处通过JNI调用InputEventReceiver的dispatchInputEvent方法

先来看一下如何接收数据的

c 复制代码
//frameworks\native\libs\input\InputTransport.cpp
status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,
                                nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
	//省略
	while (!*outEvent) {
        if (mMsgDeferred) {
            // mMsg contains a valid input message from the previous call to consume
            // that has not yet been processed.
            mMsgDeferred = false;
        } else {
            // Receive a fresh message.
            status_t result = mChannel->receiveMessage(&mMsg);//1

		//省略
	switch (mMsg.header.type) {
		case InputMessage::Type::MOTION: {
			//省略
			 	MotionEvent* motionEvent = factory->createMotionEvent();
                if (!motionEvent) return NO_MEMORY;

                updateTouchState(mMsg);
                initializeMotionEvent(motionEvent, &mMsg);//2
                *outSeq = mMsg.body.motion.seq;
                *outEvent = motionEvent;
				//省略
                break;
		}

}

注释1处接收数据,接收到的数据是InputMessage对象。注释2处根据读取到的InputMessage,创建motionEvent对象

c 复制代码
//frameworks\native\libs\input\InputTransport.cpp
status_t InputChannel::receiveMessage(InputMessage* msg) {
    ssize_t nRead;
    do {
        nRead = ::recv(mFd.get(), msg, sizeof(InputMessage), MSG_DONTWAIT);//读fd
    } while (nRead == -1 && errno == EINTR);

consume方法得到数据并将数据封装成motionEvent对象后,回到consumeEvents方法,继续调用InputEventReceiver的dispatchInputEvent方法

c 复制代码
//frameworks\base\core\java\android\view\InputEventReceiver.java
private void dispatchInputEvent(int seq, InputEvent event) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event);
}

调用onInputEvent方法,WindowInputEventReceiver继承InputEventReceiver,调用WindowInputEventReceiver的onInputEvent方法

c 复制代码
//frameworks\base\core\java\android\view\ViewRootImpl.java
final class WindowInputEventReceiver extends InputEventReceiver {
        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
        }

        @Override
        public void onInputEvent(InputEvent event) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
            //省略
            if (processedEvents != null) {
                //省略
            } else {
                enqueueInputEvent(event, this, 0, true);//注意第二个参数传入的是当前对象
            }
        }

enqueueInputEvent

c 复制代码
//frameworks\base\core\java\android\view\ViewRootImpl.java
void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
       	//省略
        if (processImmediately) {//processImmediately传进来的是true
            doProcessInputEvents();
        } else {
            scheduleProcessInputEvents();
        }
    }

doProcessInputEvents

c 复制代码
//frameworks\base\core\java\android\view\ViewRootImpl.java
void doProcessInputEvents() {
        // Deliver all pending input events in the queue.
        while (mPendingInputEventHead != null) {
           
			//省略
            deliverInputEvent(q);
        }

       //省略
    }

deliverInputEvent

c 复制代码
//frameworks\base\core\java\android\view\ViewRootImpl.java
private void deliverInputEvent(QueuedInputEvent q) {
        //省略
        try {
            //省略
            InputStage stage;
            if (q.shouldSendToSynthesizer()) {
                stage = mSyntheticInputStage;
            } else {
                stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;//1
            }

           //省略
            if (stage != null) {
                handleWindowFocusChanged();
                stage.deliver(q);//2
            } else {
                finishInputEvent(q);
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

注释1处设置InputStage,对于触摸事件,默认是忽略输入法的,所以stage 为mFirstPostImeInputStage 对象。注释2处 调用mFirstPostImeInputStage 的deliver方法。

系统中有多个InputStage组成的一个链表,在setView方法中设置的

c 复制代码
//frameworks\base\core\java\android\view\ViewRootImpl.java
// Set up the input pipeline.
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);

deliver方法的原理就是输入事件会经过这些InputStage依次处理(调用onProcess方法),如果事件已经被上一个消费处理了,后面的stage就不会处理了。触摸事件会传递到ViewPostImeInputStage中处理

c 复制代码
//frameworks\base\core\java\android\view\ViewRootImpl.java
final class ViewPostImeInputStage extends InputStage {
        public ViewPostImeInputStage(InputStage next) {
            super(next);
        }

        @Override
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            } else {
                final int source = q.mEvent.getSource();
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                    return processPointerEvent(q);
                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                    return processTrackballEvent(q);
                } else {
                    return processGenericMotionEvent(q);
                }
            }
        }

对于触摸事件,调用processPointerEvent继续处理

c 复制代码
//frameworks\base\core\java\android\view\ViewRootImpl.java
private int processPointerEvent(QueuedInputEvent q) {
            final MotionEvent event = (MotionEvent)q.mEvent;

            mAttachInfo.mUnbufferedDispatchRequested = false;
            mAttachInfo.mHandlingPointerEvent = true;
            boolean handled = mView.dispatchPointerEvent(event);//1
            //省略
            return handled ? FINISH_HANDLED : FORWARD;
}

主要是调用mView的dispatchPointerEvent方法,这里的mView是DecorView,DecorView中没有实现该方法,在其父类View中实现

c 复制代码
//frameworks\base\core\java\android\view\View.java
@UnsupportedAppUsage
    public final boolean dispatchPointerEvent(MotionEvent event) {
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            return dispatchGenericMotionEvent(event);
        }
    }

又回到DecorView的dispatchTouchEvent方法

c 复制代码
//frameworks\base\core\java\com\android\internal\policy\DecorView.java
 @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);
    }

这里的callback就是Activity对象,调用Activity的dispatchTouchEvent方法。

java 复制代码
//frameworks/base/core/java/android/app/Activity.java
  public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {//1
            return true;
        }
        return onTouchEvent(ev);//2
    }

注释1处调用getWindow的superDispatchTouchEvent方法,getWindow返回的是一个PhoneWindow对象。注意返回值,如果返回ture的话,表明消费事件,注释2处Activity的onTouchEvent方法就不会执行。反之返回false的话使用onTouchEvent进行兜底,onTouchEvent如果是返回true,后面的InputStage就不会处理了,返回false则表明继续交给后面的InputStage处理

java 复制代码
//frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
@Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

又继续调用到DecorView的superDispatchTouchEvent方法

java 复制代码
//frameworks/base/core/java/com/android/internal/policy/DecorView.java
public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }

调用其父类ViewGroup的dispatchTouchEvent方法。在分析这个方法之前,先总结下事件是如何分发到ViewGroup的

事件是由DecorView分发给Activity,然后分发给window,最后又回到DecorView,再由DecorView分发给ViewGroup的。

ViewGroup接收到事件后,接下来就是将事件分发给具体的view了
ViewGroup事件分发

java 复制代码
//frameworks/base/core/java/android/view/ViewGroup.java
@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
      //省略
        boolean handled = false;//表明是否消费事件
        if (onFilterTouchEventForSecurity(ev)) {//是否符合安全策略
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;

            // Check for interception.
            final boolean intercepted;//是否拦截事件
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;//调用requestDisallowInterceptTouchEvent这个方法设置不允许拦截,
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev);//根据返回值判断是否允许拦截
                    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的话,直接拦截
            }
		//省略
       
            // Check for cancelation.
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;

            // Update list of touch targets for pointer down, if needed.
            final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
            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);
                        // Find a child that can receive the event.
                        // Scan children from front to back.
                        final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                        final boolean customOrder = preorderedList == null
                                && isChildrenDrawingOrderEnabled();
                        final View[] children = mChildren;
                        for (int i = childrenCount - 1; i >= 0; i--) {//遍历子iew
                            final int childIndex = getAndVerifyPreorderedIndex(
                                    childrenCount, i, customOrder);
                            final View child = getAndVerifyPreorderedView(
                                    preorderedList, children, childIndex);
							//省略
							
							//如果子view不能接收事件或者触摸点不在该view上的话,忽略这个view
                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }

                            newTouchTarget = getTouchTarget(child);//取出view的TouchTarget
                           
							//忽略
                            resetCancelNextUpFlag(child);
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {//开始分发并处理了
                                //省略
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);//进入这里表示子view消费了事件,就会设置view的TouchTarget链表,mFirstTouchTarget就不为空
                                alreadyDispatchedToNewTouchTarget = true;//设为true
                                break;
                            }
                        }
                        if (preorderedList != null) preorderedList.clear();
                    }
					//省略
                }
            }

            // Dispatch to touch targets.
            if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);//注意第三个参数为null
            } else {
                // Dispatch to touch targets, excluding the new touch target if we already
                // dispatched to it.  Cancel touch targets if necessary.
                TouchTarget predecessor = null;
                TouchTarget target = mFirstTouchTarget;
                while (target != null) {
                    final TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {//表明子view消费了事件
                        handled = true;
                    } else {
                        final boolean cancelChild = resetCancelNextUpFlag(target.child)
                                || intercepted;//判断是否是取消事件
                        if (dispatchTransformedTouchEvent(ev, cancelChild,
                                target.child, target.pointerIdBits)) {
                            handled = true;
                        }
                       //省略
        return handled;
    }

首先就是看看是不是需要拦截事件,判断是否通过requestDisallowInterceptTouchEvent这个方法,设置了ViewGroup不允许拦截,如果没有设置,再判断onInterceptTouchEvent的返回值,返回flase不表示不拦截。如果没有拦截则会遍历子view,依次使用dispatchTransformedTouchEvent处理,而如果拦截了话,也是通过dispatchTransformedTouchEvent处理,只不过传入的参数中,第3个参数为null

来看一下dispatchTransformedTouchEvent这个方法

java 复制代码
//frameworks/base/core/java/android/view/ViewGroup.java
 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);//代表取消事件的话,将action设置为ACTION_CANCEL
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);//又设置回来
            return handled;
        }

     //省略

        // Perform any necessary transformations and dispatch.
        if (child == null) {
            handled = super.dispatchTouchEvent(transformedEvent);//如果第三个参数传入的是空,则调用自己父类的dispatchTouchEvent方法处理
        } else {
            final float offsetX = mScrollX - child.mLeft;
            final float offsetY = mScrollY - child.mTop;
            transformedEvent.offsetLocation(offsetX, offsetY);
            if (! child.hasIdentityMatrix()) {
                transformedEvent.transform(child.getInverseMatrix());
            }

            handled = child.dispatchTouchEvent(transformedEvent);//继续分发给子view处理
        }

        // Done.
        transformedEvent.recycle();
        return handled;
    }

dispatchTransformedTouchEvent的含义是如果child是ViewGroup的话,就继续调用ViewGroup的dispatchTouchEvent方法继续向下分发,如果child是view的话,则调用view的dispatchTouchEvent来处理事件。如果ViewGroup拦截了事件或者ViewGroup的孩子没有消费事件的话,也会调用View的dispatchTouchEvent来处理事件。来看一下view的dispatchTouchEvent方法

java 复制代码
//frameworks/base/core/java/android/view/View.java
public boolean dispatchTouchEvent(MotionEvent event) {
        //省略

        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)) {//设置过OnTouchListener优先调用
                result = true;
            }

            if (!result && onTouchEvent(event)) {//调用onTouchEvent方法
                result = true;
            }
        }
	//省略

        return result;
    }

对于事件的处理主要是判断view是不是设置过OnTouchListener,如果设置过,则调用其onTouch方法。如果OnTouch返回true的话,表示事件在这里被消费,后面的onTouchEvent就不会被调用。如果没有设置过OnTouchListener或者设置过,但是OnTouch返回false,则onTouchEvent会被调用。

上面的几个方法内容比较多,理解起来也比较费劲,用一张图总结下ViewGroup的事件分发流程


总结

  • 可以通过重写ViewGroup的onInterceptTouchEvent方法来实现对事件的拦截
  • 可以通过调用requestDisallowInterceptTouchEvent来禁止ViewGroup对事件拦截,这个优先级更高
  • 当事件都没有被View或者ViewGroup消费的话,使用Activity的onTouchEvent进行兜底
  • UP和MOVE 事件并不会重新寻找子view,而是直接分发给接收DOWN事件的view
相关推荐
安东尼肉店4 小时前
Android compose屏幕适配终极解决方案
android
2501_916007475 小时前
HTTPS 抓包乱码怎么办?原因剖析、排查步骤与实战工具对策(HTTPS 抓包乱码、gzipbrotli、TLS 解密、iOS 抓包)
android·ios·小程序·https·uni-app·iphone·webview
feiyangqingyun6 小时前
基于Qt和FFmpeg的安卓监控模拟器/手机摄像头模拟成onvif和28181设备
android·qt·ffmpeg
用户20187928316710 小时前
ANR之RenderThread不可中断睡眠state=D
android
煤球王子10 小时前
简单学:Android14中的Bluetooth—PBAP下载
android
小趴菜822710 小时前
安卓接入Max广告源
android
齊家治國平天下10 小时前
Android 14 系统 ANR (Application Not Responding) 深度分析与解决指南
android·anr
ZHANG13HAO10 小时前
Android 13.0 Framework 实现应用通知使用权默认开启的技术指南
android
【ql君】qlexcel10 小时前
Android 安卓RIL介绍
android·安卓·ril
写点啥呢10 小时前
android12解决非CarProperty接口深色模式设置后开机无法保持
android·车机·aosp·深色模式·座舱