系列总结回顾:
ViewGroup事件分发总结-TouchTarget
ViewGroup事件分发总结-多点触摸事件拆分
关于Android触摸事件分发机制,在日常应用层开发工作中最常接触的是ViewGroup中的事件派发。当应用窗口接收到系统传来的Event后,到将Event传入ViewGroup前还有一段路程,本文就来分析下这个过程。
文中源码基于 Android 10.0
APP侧想要从系统接收触摸事件,首先需要进行“接收器”的注册。我们知道Acitivty启动在onResume之后,会创建ViewRootImpl,然后调用它的setView方法向WindowManagerService添加窗口,而“接收器”的注册就是在这个阶段。
[ViewRootImpl#setView]
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
// ···
// 创建InputChannel用于输入事件的传输
mInputChannel = new InputChannel();
// ···
// 添加给WindowManagerService
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
// ···
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
// 存储事件的队列
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
// 初始化并注册输入事件接收器
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
// ···
}
}
}
该方法中几个比较关键的步骤:
接下来看其中第二步骤,WindowSession#addToDisplay会调用WindowManagerService的addWindow方法:
[WindowManagerService#addWindow]
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState) {
// ···
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
// 打开InputChannel(win是WindowState)
win.openInputChannel(outInputChannel);
}
// ···
}
win的实例是WindowState,一个WindowState表示一个应用端窗口。
[WindowState#openInputChannel]
void openInputChannel(InputChannel outInputChannel) {
if (mInputChannel != null) {
throw new IllegalStateException("Window already has an input channel.");
}
String name = getName();
// 创建native层InputChannel数组(包含两个InputChannel)
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
// 第一个用于输出事件
mInputChannel = inputChannels[0];
// 第二个用于接收事件
mClientChannel = inputChannels[1];
// InputWindowHandle作为具柄提供给InputManagerService,用来寻找目标窗口。
// IWindow对应一个应用侧窗口,将其作为token。
mInputWindowHandle.token = mClient.asBinder();
// outInputChannel即在应用进程创建传过来的InputChannel
if (outInputChannel != null) {
// 将mClientChannel中的实例状态转移到outInputChannel中
mClientChannel.transferTo(outInputChannel);
// 释放mClientChannel中的引用
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);
}
// 将mInputChannel和IWindow注册给InputManagerService保存
mWmService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
}
该方法中首先调用native方法打开两个InputChannel,一个用于系统端写入事件,一个用于应用端读取事件(当事件处理完毕后,应用端InputChannel会用来写入反馈,系统端InputChannel用来读取反馈)。之后将用于发送事件的InputChannel注册给InputManagerService,将用于接收事件的InputChannel传回给应用窗口。
这里的打开InputChannel指的是什么呢?进入native层方法看看:
[android_view_InputChannel#android_view_InputChannel_nativeOpenInputChannelPair]
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
std::string name = nameChars;
env->ReleaseStringUTFChars(nameObj, nameChars);
// serverChannel对应前文中的mInputChannel,clientChannel对应前文中的mClientChannel
sp<InputChannel> serverChannel;
sp<InputChannel> clientChannel;
// 分配serverChannel和clientChannel
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
if (result) {
String8 message;
message.appendFormat("Could not open input channel pair. status=%d", result);
jniThrowRuntimeException(env, message.string());
return NULL;
}
// 创建Java层InputChannel数组
jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
if (env->ExceptionCheck()) {
return NULL;
}
// 创建Java层InputChannel,将serverChannel的指针保存在InputChannel.mPtr成员中
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
std::make_unique<NativeInputChannel>(serverChannel));
if (env->ExceptionCheck()) {
return NULL;
}
// 同上
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
std::make_unique<NativeInputChannel>(clientChannel));
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
// 返回InputChannel数组
return channelPair;
}
可以看到这里创建了native层的InputChannel,再将指针保存在Java层InputChannel.mPtr成员中。
看其中的InputChannel::openInputChannelPair方法:
[InputTransport#InputChannel::openInputChannelPair]
status_t InputChannel::openInputChannelPair(const std::string& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
// 创建一对socketpair,保存在sockets中
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
// 错误处理 ···
return result;
}
// 设置sockopt读写缓存区
int bufferSize = SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
std::string serverChannelName = name;
serverChannelName += " (server)";
// 创建Server端InputChannel,并持有其中一个socketpair
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
std::string clientChannelName = name;
clientChannelName += " (client)";
// 创建Client端InputChannel,并持有其中一个socketpair
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK;
}
该方法中创建一对socketpair,这对socketpair中含有两个相通的文件描述符fd,通过向fd写数据和读数据实现数据通信。Server端InputChannel和Client端InputChannel各持有一个socketpair,达到互相之间的双向通信。
关于socketpair的详细解释可以参考《Android中socketpair双向通信详解》
到这里我们知道InputChannel其实是对socketpair的一层封装。
回到WindowState#openInputChannel方法中,在完成包含socketpair的InputChannel对的创建后,需要将其中一个交给APP侧,另一个交给InputManagerService。
通过InputChannel.transferTo方法将内部引用状态转移给之前在APP侧创建和传过来的InputChannel,该方法中调用了nativeTransferTo方法:
[android_view_InputChannel.cpp]
static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
jobject otherObj) {
if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Other object already has a native input channel.");
return;
}
// 通过Java层InputChannel.mPtr保存的地址获取native层InputChannel对象
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, obj);
// 将nativeInputChannel的地址设置给APP侧Java层InputChannel.mPtr
android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
// 清空自身的InputChannel.mPtr的值
android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
}
转移对象就是通过更改InputChannel.mPtr的值来实现。
当转移完对象后,就会进行InputManagerService侧的注册,在registerInputChannel方法中又会调用nativeRegisterInputChannel方法,传入InputChannel。
[com_android_server_input_InputManagerService.cpp]
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobject inputChannelObj, jint displayId) {
// 获取native层InputManagerService
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
// 获取native层InputChannel
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
// ···
// 注册进native层InputManagerService
status_t status = im->registerInputChannel(env, inputChannel, displayId);
// ···
}
该方法中取出native层对应的对象,native层InputManagerService的registerInputChannel方法中会把InputChannel注册给它的InputDispatcher。
接着看InputDispatcher#registerInputChannel方法:
[InputDispatcher.cpp]
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
int32_t displayId) {
// ···
{ // acquire lock
std::scoped_lock _l(mLock);
// ···
// 创建Connection封装InputChannel
sp<Connection> connection = new Connection(inputChannel, false /*monitor*/);
// 获取InputChannel中socketpair的文件描述符
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
// Looper监听fd可读事件,当InputChannel对端写入数据时会触发handleReceiveCallback回调
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
// Wake the looper because some connections have changed.
mLooper->wake();
return OK;
}
该方法中创建Connection用来封装InputChannel,并保存在mConnectionsByFd和mInputChannelsByToken集合中。并让InputDispatcherThread中创建的Looper监听InputChannel中fd的可读事件。
Connection中有几个重要成员:
到这里便完成了InputChannel的初始化,创建了一对InputChannel分别保存在InputManagerService侧和应用窗口侧,从而实现传输链路的连接,之后双方可以通过InputChannel中的socketpair进行双向通信。
InputEventReceiver用于APP侧接收到数据后响应事件回调。回到ViewRootImpl#setView中,这里创建其子类WindowInputEventReceiver,并传入初始化完成的InputChannel和主线程Looper。
在InputEventReceiver的构造函数中,会利用InputChannel和主线程Looper中的MessageQueue进行初始化操作,其中会调用nativeInit方法:
[android_view_InputEventReceiver.cpp]
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
// 通过InputChannel.mPtr成员获得native层InputChannel对象
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
// ···
// 通过MessageQueue.mPtr成员获得native层MessageQueue对象
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
// ···
// 创建native层InputEventReceiver,封装Java层InputEventReceiver、InputChannel、MessageQueue
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
// 初始化,使Looper监听fd中可读事件
status_t status = receiver->initialize();
// ···
receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
// 返回native层InputEventReceiver指针
return reinterpret_cast<jlong>(receiver.get());
}
该方法中主要是创建了native层的InputEventReceiver,并返回其地址给Java层保存。在它的初始化操作中,使用MessageQueue对应的Looper监听InputChannel中fd的ALOOPER_EVENT_INPUT可读事件。
输入事件“接收器”的注册过程,其实就是创建了一对InputChannel,分别保存在InputManagerService侧和应用窗口侧,利用其进行双端数据传输。然后又创建WindowInputEventReceiver,用于应用窗口侧在接收到事件后,进一步分发处理。
这里不讨论InputManagerService中的事件发送流程,仅从触发事件接收开始分析。
当InputManagerService通过InputChannel的fd写入事件数据后,应用窗口侧的InputChannel就会产生可读事件,将唤醒应用侧Looper(NativeInputEventReceiver初始化时注册了fd监听和回调),然后触发LooperCallback的handleEvent回调(NativeInputEventReceiver继承LooperCallback)。
[android_view_InputEventReceiver.cpp]
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
// ···
if (events & ALOOPER_EVENT_INPUT) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
return status == OK || status == NO_MEMORY ? 1 : 0;
}
// ···
}
判断类型如果是ALOOPER_EVENT_INPUT,则调用consumeEvents方法进一步处理。
[android_view_InputEventReceiver.cpp]
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
// ···
// 循环不断的处理(如果有事件)
for (;;) {
uint32_t seq;
InputEvent* inputEvent;
// 从InputChannel中读取一条数据,封装在InputEvent中
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent);
// ···
if (!skipCallbacks) {
// ···
jobject inputEventObj;
// 判断事件类型,创建对应事件对象
switch (inputEvent->getType()) {
case AINPUT_EVENT_TYPE_KEY:
// 按键类型事件
inputEventObj = android_view_KeyEvent_fromNative(env,
static_cast<KeyEvent*>(inputEvent));
break;
case AINPUT_EVENT_TYPE_MOTION: {
// 触摸类型事件
MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);
if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
*outConsumedBatch = true;
}
inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
break;
}
default:
assert(false); // InputConsumer should prevent this from ever happening
inputEventObj = NULL;
}
if (inputEventObj) {
// 调用Java层方法分发事件对象
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching input event.");
skipCallbacks = true;
}
env->DeleteLocalRef(inputEventObj);
} else {
// ···
}
}
if (skipCallbacks) {
mInputConsumer.sendFinishedSignal(seq, false);
}
}
}
可以看到,这里面循环不断的从InputChannel中读取数据封装成InputEvent对象,并根据事件类型(按键、触摸)创建对应的事件对象,之后调用Java层WindowInputEventReceiver的dispatchInputEvent方法进行事件派发。
事件接收是通过先前注册的主线程Looper监听fd来实现,收到事件后封装成对应类型的触摸事件,调用Java层InputEventReceiver#dispatchInputEvent方法传递事件对象进行派发。
[InputEventReceiver#dispatchInputEvent]
private void dispatchInputEvent(int seq, InputEvent event) {
// 保存event序列号,用于事件处理完毕后再通知InputManagerService
mSeqMap.put(event.getSequenceNumber(), seq);
// 处理事件
onInputEvent(event);
}
WindowInputEventReceiver重写了onInputEvent方法:
[WindowInputEventReceiver#onInputEvent]
public void onInputEvent(InputEvent event) {
// ···
// 事件入队
enqueueInputEvent(event, this, 0, true);
// ···
}
这里调用ViewRootImpl的enqueueInputEvent方法先将事件入队。
[ViewRootImpl#enqueueInputEvent]
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
// 从对象缓存池获取一个QueuedInputEvent,封装InputEvent
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
// Always enqueue the input event in order, regardless of its time stamp.
// We do this because the application or the IME may inject key events
// in response to touch events and we want to ensure that the injected keys
// are processed in the order they were received and we cannot trust that
// the time stamp of injected events are monotonic.
// 加入队列尾
QueuedInputEvent last = mPendingInputEventTail;
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
mPendingInputEventCount += 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
// 此时processImmediately为true,表示立即处理
if (processImmediately) {
// 直接开始处理
doProcessInputEvents();
} else {
// 通过handler发送一个MSG_PROCESS_INPUT_EVENTS消息处理
scheduleProcessInputEvents();
}
}
加入队列后就立即开始处理。
接着看doProcessInputEvents方法:
[ViewRootImpl#doProcessInputEvents]
void doProcessInputEvents() {
// Deliver all pending input events in the queue.
// 遍历事件队列
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null;
mPendingInputEventCount -= 1;
// ···
// 依次分发队列头事件
deliverInputEvent(q);
}
// We are done processing all input events that we can process right now
// so we can clear the pending flag immediately.
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
// 事件全部分发完毕,取消MSG_PROCESS_INPUT_EVENTS消息
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
该方法中会依次将队列中的事件全部进行分发。
[ViewRootImpl#deliverInputEvent]
private void deliverInputEvent(QueuedInputEvent q) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
q.mEvent.getSequenceNumber());
// ···
InputStage stage;
// ···
// 获取InputStage
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
// ···
if (stage != null) {
handleWindowFocusChanged();
// 由InputStage进行具体的事件分发处理
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
该方法中获取InputStage后,由InputStage处理事件分发。
InputStage是事件分发责任链中的一个基本单元。输入事件可能会经过多个InputStage,直到有InputStage拦截处理,或者都没有处理。
InputStage责任链的设置也是在ViewRootImpl的setView分发中:
[ViewRootImpl#setView]
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
// ···
// Set up the input pipeline.
CharSequence counterSuffix = attrs.getTitle();
// 综合处理未分发的事件
mSyntheticInputStage = new SyntheticInputStage();
// 分发给view hierarchy,优先级在输入法之后
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
// 分发给native activity,优先级在输入法之后
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
// 优先级在输入法之后
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
// 分发给输入法(不支持触摸事件)
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);
// 分发给view hierarchy,优先级在输入法之前(不支持触摸事件)
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
// 分发给native activity,优先级在输入法之前(不支持触摸事件)
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
// mFirstInputStage是优先级最高的InputStage
mFirstInputStage = nativePreImeStage;
// mFirstPostImeInputStage是输入法之后优先级最高的InputStage
mFirstPostImeInputStage = earlyPostImeStage;
}
可以看到通过组合包装的方式创建了InputStage责任链,越早创建的InputStage越后执行。
这里关心ViewPostImeInputStage,它负责分发事件到视图树,看它的deliver分发:
[ViewPostImeInputStage#deliver]
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
// 如果标记FLAG_FINISHED则转到下一个InputStage
forward(q);
} else if (shouldDropInputEvent(q)) {
// 如果需要丢弃则标记FLAG_FINISHED,然后转给下一个InputStage
finish(q, false);
} else {
// onProcess分发处理事件并返回是否处理的结果,apply会根据结果继续向下转发
apply(q, onProcess(q));
}
}
直接看onProcess方法:
[ViewPostImeInputStage#onProcess]
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方法:
[ViewPostImeInputStage#processPointerEvent]
private int processPointerEvent(QueuedInputEvent q) {
// 事件对象转换成MotionEvent
final MotionEvent event = (MotionEvent)q.mEvent;
// ···
// 调用了View的dispatchPointerEvent方法
boolean handled = mView.dispatchPointerEvent(event);
// ···
return handled ? FINISH_HANDLED : FORWARD;
}
mView就是在ViewRootImpl#setView时传入的DecorView,这里调用了它的dispatchPointerEvent方法进行MotionEvent的分发。
当MotionEvent传给DecorView后,还没有直接派发给我们设置的视图树。
dispatchPointerEvent方法中又调用了dispatchTouchEvent方法:
[DecorView#dispatchTouchEvent]
public boolean dispatchTouchEvent(MotionEvent ev) {
// 获取PhoneWindow中的Callback
final Window.Callback cb = mWindow.getCallback();
// 正常情况下会执行cb.dispatchTouchEvent(ev)
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
可以看到DecorView并不会直接把MotionEvent派发给Content View,而是转发给Window.Callback,这个Callback就是Activity实例(Activity实现了Window.Callback接口,Activity在attach方法中创建PhoneWindow时设置Callback)。
进入Activity:
[Activity#dispatchTouchEvent]
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
// 又转发给PhoneWindow处理
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
该方法中又优先将MotionEvent交由PhoneWindow处理,若没有处理则再通过onTouchEvent由自身处理。
进入PhoneWindow:
[PhoneWindow#superDispatchTouchEvent]
public boolean superDispatchTouchEvent(MotionEvent event) {
// 由调用了DecorView
return mDecor.superDispatchTouchEvent(event);
}
这里又调用了DecorView,不同的是调用的是superDispatchTouchEvent方法,该方法中会调用super.dispatchTouchEvent,即ViewGroup的dispatchTouchEvent方法。从这里开始就正式将MotionEvent传入ViewGroup,开始ViewGroup的事件派发流程。
一个触摸事件在派发给ViewGroup前,还经过了InputManagerService-APP窗口-窗口根视图的漫长流程,其中事件传输的关键就是InputChannel和InputEventReceiver。