你是怎么收到 TouchEvent 的?

基于Android 9 分析

InputEventReceiver

框架层可见的点击事件从哪里来?

所有的事件都是由InputEventReceiver 先接受再分发出来的,

1
2
3
4
5
6
 // Called from native code.
@SuppressWarnings("unused")
private void dispatchInputEvent(int seq, InputEvent event, int displayId) {
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event, displayId);
}

那native code 又是怎么才会调上来的呢?

那就是在 InputEventReceiver 初始化的时候,需要注册。就是在 nativeInit 方法里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Creates an input event receiver bound to the specified input channel.
*
* @param inputChannel The input channel.
* @param looper The looper to use when invoking callbacks.
*/
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null");
}
if (looper == null) {
throw new IllegalArgumentException("looper must not be null");
}

mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);

mCloseGuard.open("dispose");
}

InputChannel

  • An input channel specifies the file descriptors used to send input events to a window in another process. It is Parcelable so that it can be sent to the process that is to receive events. Only one thread should be reading from an InputChannel at a time.
  • 输入通道指定用于在另一个进程中将输入事件发送到窗口的文件描述符。 它是Parcelable,因此可以将其发送到要接收事件的进程。 一次只能一个线程从InputChannel读取。
1
2
private static native long nativeInit(WeakReference<InputEventReceiver> receiver,
InputChannel inputChannel, MessageQueue messageQueue);

先来看一 InputEventReceiver 的初始化 又是谁调用的呢?

在我们启动App的时候,最先由AMS跨进程传输的一个ClientTransaction,客户端进程ApplicationThread接收,然后发送到主线程ActivityThread,最后由TransactionExecutor统一解析。AMS封装并传输ClientTransaction,统一接口;客户端进程接收ClientTransaction并使用TransactionExecutor解析AMS的请求,再根据ActivityLifecycleItem执行不同的代码。Activity的生命周期就是 transaction.getLifecycleStateRequest() 获取的,然后再执行响应的生命周期回调。

调用栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<init>(InputChannel, Looper):60, InputEventReceiver (android.view)<init>(ViewRootImpl, InputChannel, Looper):7190, 

//最终调用到 InputEventReceiver 初始化
ViewRootImpl$WindowInputEventReceiver (android.view)

//这里会初始化几个inputStage
setView(View, WindowManager$LayoutParams, View):847,

ViewRootImpl (android.view)


//DecorView 添加 我们自己写的 MainActivity
addView(View, ViewGroup$LayoutParams, Display, Window):356, WindowManagerGlobal (android.view)

//WindowManagerGlobal 添加 DecorView
addView(View, ViewGroup$LayoutParams):93, WindowManagerImpl (android.view)

//执行 Resume
handleResumeActivity(IBinder, boolean, boolean, String):3868, ActivityThread (android.app)

execute(ClientTransactionHandler, IBinder,PendingTransactionActions):51, ResumeActivityItem (android.app.servertransaction)

//executeLifecycleState方法是用来改变Activity的生命周期状态的
//transaction.getLifecycleStateRequest() 获取的
executeLifecycleState(ClientTransaction):145,

//然后交给TransactionExecutor 统一调度
TransactionExecutor (android.app.servertransaction)
execute(ClientTransaction):70, TransactionExecutor (android.app.servertransaction)


//首先是ActivityThread 收到一个ClientTransaction的消息
handleMessage(Message):1808, ActivityThread$H (android.app)
dispatchMessage(Message):106, Handler (android.os)
loop():193, Looper (android.os)
main(String[]):6669, ActivityThread (android.app)
invoke(Object, Object[]):-1, Method (java.lang.reflect)
run():493, RuntimeInit$MethodAndArgsCaller (com.android.internal.os)
main(String[]):858, ZygoteInit (com.android.internal.os)

我们可以看到 在 android.view.ViewRootImpl#setView当中调用了,这个looper就是主线程的looepr了。这个InputChannel 实际上指向我们自己写的MainActivity (client)

1
2
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());

再来看一 nativeInit 又做了什么呢?

在NativeInputEventReceiver的nativeInit方法中,创建了NativeInputEventReceiver对象,并调用它的initialize方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
...

//用传过来的inputChannel和messageQueue构建一个NativeInputEventReceiver
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
//然后调用它的初始化方法
status_t status = receiver->initialize();
...
}


status_t NativeInputEventReceiver::initialize() {
setFdEvents(ALOOPER_EVENT_INPUT);
return OK;
}
  • mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
  • 实际上就把JAVA层的Looper关联ALOOPER_EVENT_INPUT
1
2
3
4
5
6
7
8
9
10
11
void NativeInputEventReceiver::setFdEvents(int events) {
if (mFdEvents != events) {
mFdEvents = events;
int fd = mInputConsumer.getChannel()->getFd();
if (events) {
mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
}
}
  • fd,fd即inputChannel的socket fd,Looper会侦测该fd的状态
  • events,即传入的ALOOPER_EVENT_INPUT,只有fd的状态是INPUT的时候才会触发调用LooperCallback中的handleEvent方法
  • this,即NativeInputEventReceiver,当fd状态为Input时,NativeInputEventReceiver中的handleEvent方法会被调用

在consumeEvents内,我们能看到调用了InputConsume::consume来接收InputDispatcher发送过来的事件

1
2
3
4
5
6
7
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
for (;;) {
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent);
}
}

最终回调到 java 层的 InputEventReceiver#dispatchInputEvent 。

1
2
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);

通过上述的JNI调用,会调用到WindowInputEventReceiver的dispatchInputEvent方法,不过由于WindowInputEventReceiver并没有自己实现这个方法,因此会调用父类InputEventReceiver::dispatchInputEvent,内部会真正调用到android.view.ViewRootImpl.WindowInputEventReceiver#onInputEvent

接下来就是JAVA层,一层层分发事件了

android.view.ViewRootImpl.WindowInputEventReceiver#onInputEvent

1
2
3
public void onInputEvent(InputEvent event, int displayId) {
enqueueInputEvent(event, this, 0, true);
}

android.view.ViewRootImpl#enqueueInputEvent(android.view.InputEvent, android.view.InputEventReceiver, int, boolean)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
adjustInputEventForCompatibility(event);
// 池 包装,类似 obtainMessage
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
QueuedInputEvent last = mPendingInputEventTail;

//入队到 所有 等候 的InputEvent
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
//等候的事件+1
mPendingInputEventCount += 1;

//打个日志
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);


//是不是立即处理改事件
if (processImmediately) {
doProcessInputEvents();
} else {
//还是加到调度器里面
//这里实际上是发送异步消息 mHandler.sendMessage(msg);
scheduleProcessInputEvents();
}
}

从 enqueueInputEvent(event, this, 0, true) 看 ,这里的事件是processImmediately = true的,所以进一步再追一下 doProcessInputEvents()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void doProcessInputEvents() {
// Deliver all pending input events in the queue.
// 分发所有的 等候 InputEvent
while (mPendingInputEventHead != null) {

//出队
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null;

//count--
mPendingInputEventCount -= 1;

//打个日志
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);

long eventTime = q.mEvent.getEventTimeNano();
long oldestEventTime = eventTime;
if (q.mEvent instanceof MotionEvent) {
MotionEvent me = (MotionEvent)q.mEvent;
if (me.getHistorySize() > 0) {
oldestEventTime = me.getHistoricalEventTimeNano(0);
}
}
mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);

//这里具体 去分发处理该次事件
deliverInputEvent(q);
}

// We are done processing all input events that we can process right now
// so we can clear the pending flag immediately.
// 因为我们已经 分发完了 所有等候 的 InputEvent
//清除wath = MSG_PROCESS_INPUT_EVENTS 的消息
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}

接下来再继续追一下 android.view.ViewRootImpl#deliverInputEvent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 private void deliverInputEvent(QueuedInputEvent q) {
//又打个日志
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",q.mEvent.getSequenceNumber());


// 这个描述是 方便debug的,不影响后面的处理
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
}


InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}


if (q.mEvent instanceof KeyEvent) {
mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
}

//上面决定将事件派发到那个InputStage处理
if (stage != null) {
handleWindowFocusChanged();
stage.deliver(q);
} else {
finishInputEvent(q);
}
}

deliverInputEvent先判断将事件派发到那个InputStage,然后调用该InputState的deliver方法

那么InputStage 又是 什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
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;
...
}

InputStage 是在setView()的时候创建的,也就是在Activity的onResume()

通过一路的 forward deliver ,最后走到apply 的 onProcess 方法中去。也就是
android.view.ViewRootImpl.ViewPostImeInputStage#onProcess

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
}
}
}

那么再来看下一

  • android.view.ViewRootImpl.ViewPostImeInputStage#processPointerEvent 里面又是怎么发的?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;

mAttachInfo.mUnbufferedDispatchRequested = false;
mAttachInfo.mHandlingPointerEvent = true;


//mView 就是 android.view.ViewRootImpl#setView 的时候传入的View,也就是 DecorView
//接下里就是 走到 DecorView的dispatchPointerEvent 中去了
//标记1,这里第一次 把事件发到 DecorView 中
boolean handled = mView.dispatchPointerEvent(event);
maybeUpdatePointerIcon(event);
maybeUpdateTooltip(event);
mAttachInfo.mHandlingPointerEvent = false;
if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
mUnbufferedInputDispatch = true;
if (mConsumeBatchedInputScheduled) {
scheduleConsumeBatchedInputImmediately();
}
}
return handled ? FINISH_HANDLED : FORWARD;
}

当调用到 DecorView 的 dispatchPointerEvent ,实际上是 android.view.View#dispatchPointerEvent ,接下来的问题实际上就是 view的事件分发了?

1
2
3
4
5
6
7
8
9
 public final boolean dispatchPointerEvent(MotionEvent event) {

//是点击事件,转到dispatchTouchEvent
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}

转接到

  • com.android.internal.policy.DecorView#dispatchTouchEvent
1
2
3
4
5
6
7
8
9
public boolean dispatchTouchEvent(MotionEvent ev) {

//Activity实现了Window.Callback接口 android.app.Activity#attach中 mWindow.setCallback(this); 所以这个callback指向Activity
// mWindow 就是 PhoneWindow ,在 DecorView 初始化的时候传入的
final Window.Callback cb = mWindow.getCallback();
// 标记2,这里从DecorView 把事件转发到 Activity 中
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}

最后cb.dispatchTouchEvent(ev) 就相当于 走入到了

  • android.app.-Activity#dispatchTouchEvent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
//事件又被传递到了PhoneWindow中去分发这个事件
//如果被消费了,直接return
//标记3,这里把事件从Activty转发到phoneWindow中去了
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}

// 如果上面没有被消费,则进入Activity 的 onTouchEvent
return onTouchEvent(ev);
}

Activity 的 getWindow() 就是 android.app.Activity#attach中的mWindow ,也就是 PhoneWindow。

  • com.android.internal.policy.PhoneWindow#superDispatchTouchEvent
1
2
3
4
5
 @Override
public boolean superDispatchTouchEvent(MotionEvent event) {
//纳尼,又 转接到了 DecorView 中去
return mDecor.superDispatchTouchEvent(event);
}

从上面的标记1,2,3 可以看到,首先事件从 android.view.ViewRootImpl.ViewPostImeInputStage#processPointerEvent 发到 DecorView ,DecorView 再分发事件的时候,通过 Window 获取callback 拿到 Activity ,转交给Activity 去发送,但是Activity又通过 getWindow,又把事件转交给Window ,Window 最后又通过mDecor.superDispatchTouchEvent(event) 转交给 了DecorView。搞半天?最后还是回到 android.view.ViewRootImpl.ViewPostImeInputStage#processPointerEvent 的 mView.dispatchPointerEvent(event)中?

显然不是 第一次调用的是 DecorView.dispatchPointerEvent(event) ,第二次回到 DecorView是调用的 DecorView.superDispatchTouchEvent(event);也就调用到 DecorView 的父类 DispatchTouchEvent ,最后也就是 android.view.ViewGroup#dispatchTouchEvent的了。

一小段调用栈

1
2
3
4
5
6
7
8
9
---
dispatchTouchEvent(MotionEvent):2543, ViewGroup (android.view)//ViewGroup 分发事件
superDispatchTouchEvent(MotionEvent):440, DecorView (com.android.internal.policy)//第二次
superDispatchTouchEvent(MotionEvent):1830, PhoneWindow (com.android.internal.policy)
dispatchTouchEvent(MotionEvent):3400, Activity (android.app)
dispatchTouchEvent(MotionEvent):398, DecorView (com.android.internal.policy)//第一次
dispatchPointerEvent(MotionEvent):12752, View (android.view)
processPointerEvent(ViewRootImpl$QueuedInputEvent):5106,、
---

辗转的原因?来自 https://www.jianshu.com/p/b7cef3b3e703

为什么在InputStage.processPointerEvent()中不直接把事件传递给Activity,而是这样来回绕一圈。这样DecorView -> Activity -> PhoneWindow -> DecorView的来回绕一圈不是很折腾吗?

首先,为了解耦,ViewRootImpl并不知道有Activity这种东西存在!不知道!它只是持有了DecorView。所以,想要直接把触摸事件送到Activity.dispatchTouchEvent() 是不行的。

那么,既然触摸事件已经到了Activity.dispatchTouchEvent()中了,为什么不直接分发给DecorView ,而是要通过PhoneWindow 来间接发送呢?因为Activity 不知道有DecorView 这种奇怪的东西存在啊!不知道!但是,Activity持有PhoneWindow ,而PhoneWindow当然知道自己的窗口里有些什么了,所以能够把事件派发给DecorView 。你看,在Android中,Activity并不知道自己的Window中有些什么,这样耦合性就很低了。我们换一个Window试试?不管Window里面的内容如何,只要Window任然符合Activity制定的标准,那么它就能在Activity中很好的工作。这就是解耦所带来的扩展性的好处。

作者:CoorChice
链接:https://www.jianshu.com/p/b7cef3b3e703
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

ViewGroup 的事件分发流程?

首先分清楚 3个方法

  • dispatchTouchEvent() 分发器,决定事件怎么分发,会先询问onInterceptTouchEvent(),如果拦截了 进一步到 自己的 onTouchEvent() 中去。如果没有拦截,转发到子view的dispatchTouchEvent()
  • onInterceptTouchEvent() 拦截器 是ViewGroup特有的方法,可以判断和拦截要不要将事件下发到子view去处理。
  • onTouchEvent() 处理器。最后的一个子view 收到事件将会直接由【分发器】分发到【处理器】中。如果处理了返回true,如果不处理返回false,那么事件将传递到它的父级的【处理器】中。

不管个层级的【处理器】,如果处理事件返回true,一次触摸事件就结束了。如果不处理事件,返回false,就是事件还没有被消费,就会传送到上一级的【处理器】中。最终,如果DecorView的【处理器】也不打算处理事件,那么事件将会被发送到Activity的【处理器】中处理。

说白了,就是先交给子view优先消费,消费不了,返回了,我再消费。

那么ViewGroup dispatchTouchEvent()又是怎么把事件发到child view中去的呢?

  • android.view.ViewGroup#dispatchTouchEvent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Find a child that can receive the event.
// Scan children from front to back.
// 找一个可以接受这个事件的子view,从后面向前找,找到一个就结束
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);

// If there is a view that has accessibility focus we want it
// to get the event first and if not handled we will perform a
// normal dispatch. We may do a double iteration but this is
// safer given the timeframe.
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}

//该次点击是不是落在了 子view的 布局范围之中,且子view 可以接受点击,比如可见
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
  • 可能存在的问题,就是布局重叠的时候,收不到响应?

最后再子view的 android.view.View#onTouchEvent中,包装成一个 mPerformClick ,最后通过 mHandler.post(action) 把这个 action 发到 主线程的looper 里面去处理了,然后looepr 处理消息,调用action ,就是我们set的OnClickListener里面去了。

  • android.os.Handler#dispatchMessage
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void dispatchMessage(Message msg) {
// msg.callback = performClick
//开始处理 处理 Callback
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
  • 最后 调到 message.callback.run();
  • 然后 在run() 调用 performClick() 中的 mOnClickListener.onClick(this);

最后我们 就开始我们的OnClick 逻辑了


总结 最后一张大图

感谢

[Android] 输入系统(二)] https://www.cnblogs.com/TaigaCon/p/4750349.html

作者:CoorChice
链接:https://www.jianshu.com/p/b7cef3b3e703
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。