在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