Touch 事件是如何传递到 Activity ?
一、概述
熟悉事件分发的人都知道,事件是从 Activity.dispatchTouchEvent()
开始向下传递的,那么 Activity 中的 Touch 事件又是从哪里接受到的呢?在这篇文章中,我们来简单分析一下 Activity 接收 Touch 事件的流程。
版本: Android SDK 29
关联文章:
1.《Window系列 (一) — WindowManager 详解》
二、流程分析
对于一块陌生的代码,去猜测它的调用流程是困难的,这里有个小技巧,可以利用 Thread.dumpStack()
来查看当前线程的方法调用栈 (即 MainActivity.dispatchTouchEvent()
方法的调用栈)。
代码如下所示:
public class MainActivity extends AppCompatActivity {
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// 使用 Thread.dumpStack() 来查看 MainActivity.dispatchTouchEvent() 方法的调用栈。
Thread.dumpStack();
return super.dispatchTouchEvent(ev);
}
}
Logcat 导出的堆栈如下:
W/System.err: java.lang.Throwable: stack dump
W/System.err: at java.lang.Thread.dumpStack(Thread.java:490)
// 到这里,事件已经分发到了 Activity.dispatchTouchEvent() 方法。
W/System.err: at com.elson.example.MainActivity.dispatchTouchEvent(MainActivity.java:55)
W/System.err: at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
W/System.err: at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2483)
W/System.err: at android.view.View.dispatchPointerEvent(View.java:8820)
W/System.err: at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4750)
W/System.err: at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4608)
W/System.err: at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4098)
W/System.err: at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4151)
W/System.err: at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4117)
W/System.err: at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4254)
W/System.err: at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4125)
W/System.err: at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4311)
W/System.err: at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4098)
W/System.err: at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4151)
W/System.err: at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4117)
W/System.err: at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4125)
W/System.err: at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4098)
W/System.err: at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6559)
W/System.err: at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6533)
W/System.err: at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6486)
W/System.err: at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6739)
// 可以看出,这里是事件接收的开始。
W/System.err: at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
W/System.err: at android.os.MessageQueue.nativePollOnce(Native Method)
W/System.err: at android.os.MessageQueue.next(MessageQueue.java:148)
W/System.err: at android.os.Looper.loop(Looper.java:151)
W/System.err: at android.app.ActivityThread.main(ActivityThread.java:5775)
W/System.err: at java.lang.reflect.Method.invoke(Native Method)
W/System.err: at java.lang.reflect.Method.invoke(Method.java:372)
W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:975)
W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:770)
通过以上的堆栈信息,我们可以看出 Touch 事件从 远程服务端的接收 到 传递给 Activity 的过程大致如下:
InputEventReceiver.dispatchInputEvent()
–>ViewRootImpl.WindowInputEventReceiver.onInputEvent()
–>ViewRootImpl.deliverInputEvent()
–>ViewRootImpl.InputStage.deliver()
…这里经过一些了 InputStage 子类的传递处理(责任链模式)
…
–>ViewRootImpl.ViewPostImeInputStage.processPointerEvent()
–>View.dispatchPointerEvent()
–>PhoneWindow$DecorView.dispatchTouchEvent()
–>WindowCallbackWrapper.dispatchTouchEvent()
–>Activity.dispatchTouchEvent()
三、源码分析
下面从三个方面来分析:
- 应用进程如何接收远程服务回传的 Touch 事件?
- Touch 事件如何传递到 Activity 中?
- InputStage 的链式调用
1. 应用进程如何接收远程服务回传的 Touch 事件?
在 ViewRootImpl.setView()
中,完成了事件分发的监听操作。
ViewRootImpl
// ViewRootImpl.class
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
// 1.创建一个事件输入通道(InputChannel)。
mInputChannel = new InputChannel();
}
// ...省略代码...
if (mInputChannel != null) {
// ...省略代码...
// 2.WindowInputEventReceiver 用于接收远程服务发送的事件,此处与mInputChannel进行关联。
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());
}
// ...省略代码...
// 3.这段代码在分析 “事件如何传递到Activity”时有用。
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);
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
}
}
}
这个方法主要有三步:
- 步骤1:创建一个事件传递的通道 (
InputChannel
)。 - 步骤2:创建一个接收远程服务发送事件的类 (
WindowInputEventReceiver
)。 - 步骤3:创建一系列的 InputStage,并进行串联。
下面我们来看一下 WindowInputEventReceiver 里面是如何操作的。
WindowInputEventReceiver (ViewRootImpl 内部类)
// ViewRootImpl.WindowInputEventReceiver.class
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event) {
// 这里会接收到远程服务回传的 Touch 事件。
}
// ...省略代码...
}
我们看到 InputChannel 被传递到了 WindowInputEventReceiver 的父类中,所以我们接着往下看。
InputEventReceiver
// InputEventReceiver.class
public abstract class InputEventReceiver {
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
// ...省略代码...
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
// 将 InputChannel 传递到 Native 层。
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
mCloseGuard.open("dispose");
}
// 这个方法是被 Native 层调用的,我们可以猜测远程服务要分发 Touch 事件时,会触发这个方法来把事件回传到应用进程。
// Called from native code.
@SuppressWarnings("unused")
@UnsupportedAppUsage
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
// 这个方法在子类 WindowInputEventReceiver 中被重写。
onInputEvent(event);
}
}
小结:
- 接收事件类的注册是在添加Window的过程中(即:
ViewRootImpl.setView()
)。 - 接收远程服务分发的Touch事件的类为
WindowInputEventReceiver
。
2. Touch 事件如何传递到 Activity 中?
上面我们知道,应用进程中接收 Touch 事件最早是在 WindowInputEventReceiver.onInputEvent()
方法中,下面我们就来分析一下从接收到 Touch 事件到分发到 Activity.dispatchTouchEvent()
的流程。
时序图:
源码流程分析:
WindowInputEventReceiver (ViewRootImpl 内部类)
// ViewRootImpl.WindowInputEventReceiver.class
final class WindowInputEventReceiver extends InputEventReceiver {
// ...省略代码...
@Override
public void onInputEvent(InputEvent event) {
// ...省略代码...
// 将事件加入队列
enqueueInputEvent(event, this, 0, true);
}
}
ViewRootImpl
// ViewRootImpl.class
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
// 1. 将参数保包装成 QueuedInputEvent 事件类。
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
// ...省略加入队列的代码...
if (processImmediately) {
// 2.处理事件类型。
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
}
void doProcessInputEvents() {
// Deliver all pending input events in the queue.
// 从队列中获取事件进行处理
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
// ...省略代码...
mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
// 分发 Touch 事件
deliverInputEvent(q);
}
// ...省略代码...
}
private void deliverInputEvent(QueuedInputEvent q) {
// ...省略代码...
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
// 在 ViewRootImpl.setView() 中,我们知道:
// mFirstInputStage指向NativePreImeInputStage类;
// mFirstPostImeInputStage 指向 EarlyPostImeInputStage类。
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
// ...省略代码...
if (stage != null) {
handleWindowFocusChanged();
// 这里获取到 InputStage 子类进行链式调用来分发事件(每个子类有不同的作用)
// 根据上面的调用流程,我们知道最终会调用到 ViewPostImeInputStage.Process() 方法。
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
小结:
- 根据 Locat 中显示的调用流程,我们知道
stage.deliver(q)
调用后,最终会调用到ViewPostImeInputStage.processPointerEvent()
方法。
ViewPostImeInputStage
// ViewPostImeInputStage.class
/**
* Delivers post-ime input events to the view hierarchy.
*/
final class ViewPostImeInputStage extends InputStage {
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
// ...省略代码...
// mView 是 DecorView,最终调用了 DecorView.dispatchPointerEvent()方法。
boolean handled = mView.dispatchPointerEvent(event);
// ...省略代码...
return handled ? FINISH_HANDLED : FORWARD;
}
}
View
// View.class
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
// 执行 Touch 事件
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
DecorView
// DecorView.class
public boolean dispatchTouchEvent(MotionEvent ev) {
// cb 指向 Activity,在Activity中实现了Window.Callback接口。
// 具体给 Window 设置 Callback 的地方在 Activity.attach() 方法中,有一行 PhoneWindow.setCallback(this) 代码。
final Window.Callback cb = mWindow.getCallback();
// 所以这里最终触发了 Activity.dispatchTouchEvent() 方法。
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
到这里,从 WindowInputEventReceiver 接收到 Touch 事件到分发到 Activity 的流程就分析完了。
3. InputStage 的链式调用流程
从上面你的调用流程我们可以看到,这中间涉及到 InputStage 及其子类的一系列链式调用,下面我们来分析一下这个链式调用的流程。
InputStage 是在ViewRootImpl.setView()
方法中创建的。
ViewRootImpl
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
// ...省略代码...
// 这里使用了责任链,将 InputStage 进行串联。
CharSequence counterSuffix = attrs.getTitle();
mSyntheticInputStage = new SyntheticInputStage();
// Delivers post-ime input events to the view hierarchy.
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
// Delivers post-ime input events to a native activity.
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
// Performs early processing of post-ime input events.
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
// Delivers input events to the ime. Does not support pointer events.
InputStage imeStage = new ImeInputStage(earlyPostImeStage, "aq:ime:" + counterSuffix);
// Delivers pre-ime input events to the view hierarchy. Does not support pointer events.
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
// Delivers pre-ime input events to a native activity. Does not support pointer events.
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
}
}
}
小结:
- 从 InputStage 子类的注释,我们知道
NativePreImeInputStage、ViewPreImeInputStage、ImeInputStage
都不处理 Pointer 事件(手势事件,Touch事件属于手势事件)。 - 从
NativePreImeInputStage、ViewPreImeInputStage
与NativePostImeInputStage、ViewPostImeInputStage
类名可以看出,他们的两两配对的。
EarlyPostImeInputStage
// EarlyPostImeInputStage.class
final class EarlyPostImeInputStage extends InputStage {
public EarlyPostImeInputStage(InputStage next) {
super(next);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
} else if (q.mEvent instanceof MotionEvent) {
// 处理 MotionEvent 事件的。
return processMotionEvent(q);
}
return FORWARD;
}
}
ViewPostImeInputStage
// ViewPostImeInputStage.class
final class ViewPostImeInputStage extends InputStage {
@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); // 核心代码
}
// ...省略代码...
}
}
private int processPointerEvent(QueuedInputEvent q) {
// ...省略代码...
// 核心代码
boolean handled = mView.dispatchPointerEvent(event);
// ...省略代码...
return handled ? FINISH_HANDLED : FORWARD;
}
}
InputStage 链式调用的流程:
InputStage
// InputStage.class
abstract class InputStage {
private final InputStage mNext;
// 将下个InputStage 与当前InputStage 作关联。
public InputStage(InputStage next) {
mNext = next;
}
// 开始调用 InputStage.deliver 进行事件分发。
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);
} else if (shouldDropInputEvent(q)) {
finish(q, false);
} else {
// 执行分发逻辑
apply(q, onProcess(q)); //onProcess()默认返回FORWARD
}
}
protected int onProcess(QueuedInputEvent q) {
return FORWARD;
}
// 根据子类重写 onProcess 方法返回不同的 result 来执行不同的逻辑。
protected void apply(QueuedInputEvent q, int result) {
if (result == FORWARD) {
// 继续执行下一个
forward(q);
} else if (result == FINISH_HANDLED) {
finish(q, true);
} else if (result == FINISH_NOT_HANDLED) {
finish(q, false);
} else {
throw new IllegalArgumentException("Invalid result: " + result);
}
}
/**
* Forwards the event to the next stage.
*/
protected void forward(QueuedInputEvent q) {
onDeliverToNext(q);
}
/**
* Called when an event is being delivered to the next stage.
*/
protected void onDeliverToNext(QueuedInputEvent q) {
if (DEBUG_INPUT_STAGES) {
Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q);
}
if (mNext != null) {
// 然后执行下一个 InputStage 的分发流程。
mNext.deliver(q);
} else {
finishInputEvent(q);
}
}
}