0. 相关分享
Android源码分析 - InputManagerService与触摸事件
1. 接收Input系统发送来的事件
时序图源:稀土掘金
在注册Window的时候,来到ViewRootImpl,其中不仅发起窗口注册,还开启了输入事件的监听:
//ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {
res = mWindowSession.addToDisplayAsUser();//注册window
//注册输入事件监听器
mInputEventReceiver= new WindowInputEventReceiver(inputChannel, Looper.myLooper());
}
在ViewRootImpl的内部类WindowInputEventReceiver中重写了InputEventReceiver的onInputEvent()方法,处理事件分发:
//ViewRootImpl.java
final class WindowInputEventReceiver extends InputEventReceiver {
@Override
public void onInputEvent(InputEvent event) {
List<InputEvent> processedEvents;
try {
processedEvents =
mInputCompatProcessor.processInputEventForCompatibility(event);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (processedEvents != null) {
if (processedEvents.isEmpty()) {
finishInputEvent(event, true);
} else {
//如果有输入事件,分发
for (int i = 0; i < processedEvents.size(); i++) {
//入队
enqueueInputEvent(
processedEvents.get(i), this,
QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
}
}
} else {
enqueueInputEvent(event, this, 0, true);
}
}
//...
}
接收到输入事件之后,来到ViewRootImpl内部类QueuedInputEvent,一个QueuedInputEvent存着一个InputEvent和对应的InputEventReceiver,ViewRootImpl.enqueueInputEvent()将消息存入:
//ViewRootImpl
private static final class QueuedInputEvent {
public QueuedInputEvent mNext;//复用池
public InputEvent mEvent;
public InputEventReceiver mReceiver;
public int mFlags;
}
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);//获取一个可用的QueuedInputEvent对象
QueuedInputEvent last = mPendingInputEventTail;//本身QueuedInputEvent也是个队列,队尾为mPendingInputEventTail
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;//添加在队尾
mPendingInputEventTail = q;
}
//事件+1
mPendingInputEventCount += 1;
//立即分发来到doProcessInputEvents();
if (processImmediately) {
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
}
由于传入时,processImmediately=true,所以来到doProcessInputEvents立即分发事件:
//ViewRootImpl.java
void doProcessInputEvents() {
//一个个出队,直到所有事件都处理完
while (mPendingInputEventHead != null) {
//取出队头,(队列先进先出,队头先出)
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null;
mPendingInputEventCount -= 1;
mViewFrameInfo.setInputEvent(mInputEventAssigner.processEvent(q.mEvent));
//分发事件
deliverInputEvent(q);
}
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
顾名思义 deliverInputEvent将会将事件分发到相关的处理者身上,不过这里并不是直接传给View,而是先交给InputState:
//ViewRootImpl.java
private void deliverInputEvent(QueuedInputEvent q) {
try {
//这个mFirstPostImeInputState和mFirstInputState在setView()的时候进行了初始化,对应的SynctheticInputState是ViewRootImpl的内部类,被逐层包装在InputStage中(单向链表,deliver()方法责任链模式分发QueuedInputEvent给下一个InputState),最终会分发到SynctheticInputState
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
if (q.mEvent instanceof KeyEvent) {//如果是按键
try {
mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
if (stage != null) {
//责任链模式地分发QueuedInputEvent事件
handleWindowFocusChanged();
stage.deliver(q);
} else {
finishInputEvent(q);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
这里的InputState通过责任链的方式,将QueuedInputEvent传递给InputState链表,逐个处理,首先来看到在ViewRootImpl的setView()中有哪些InputState:
//ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {
// Set up the input pipeline.
CharSequence counterSuffix = attrs.getTitle();
//最底层
mSyntheticInputStage = new SyntheticInputStage();
//ViewPostImeState接在链头
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
//NativePostImeState接在链头
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;
}
事件分发的时候,会通过deliver()方法分发事件,并以责任链模式传递给下一个InputState处理。先来看到InputState的deliver()方法,然后我们再直接看到ViewPostImeState处理输入事件。责任链主要做了几件事:
- 通过
deliver(QueuedInputEvent q)
, 进入事件在链上传递 onProcess(q)
, 处理事件apply(q,result)
, 事件传递给下一个处理者mNext.deliver(q)
,或者直接终止事件传递finishInputEvent(q)
//ViewRootImpl.InputStage.java
//1. 从inputState.deliver()为入口开始处理事件并传递责任链
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);
} else if (shouldDropInputEvent(q)) {
finish(q, false);
} else {
traceEvent(q, Trace.TRACE_TAG_VIEW);
final int result;
try {
//处理事件
result = onProcess(q);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
//继续分发事件
apply(q, result);
}
}
//2. onProcess()处理完事件后,apply()交给下一个处理者
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);
}
}
//交给下一个处理者
protected void forward(QueuedInputEvent q) {
onDeliverToNext(q);
}
//3. 最后通过mNext.deliveer()交给责任链下一个InputState处理事件
protected void onDeliverToNext(QueuedInputEvent q) {
if (mNext != null) {
//调用下一个InputState的deliver()方法
mNext.deliver(q);
} else {
finishInputEvent(q);
}
}
看完责任链,我们直接看到ViewPostImeInputState,它的onProcess()真正将事件传递下来:
//ViewRootImpl.ViewPostImeInputState.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);
}
}
}
}
我们来看看分发PointerEvent也就是触摸事件,**在这里,我们看到了熟悉的mView.dispatchPointerEvent()**事件分发:
//ViewRootImpl.ViewPostImeInputStage.java
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
mHandwritingInitiator.onTouchEvent(event);
mAttachInfo.mUnbufferedDispatchRequested = false;
mAttachInfo.mHandlingPointerEvent = true;
//这个mView就是DecorView,在setView的时候传入的。
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中途继承自View,看到 dispatchPointerEvent() 方法:
//View.java
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
//如果是触摸事件,分发触摸事件
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
再往后,就进入了我们熟知的View树的触摸事件传递了。
2. View树的触摸事件传递
2.1 DecorView
如果PhoneWindow设置了Callback的话,优先通过Callback进行dispatchTouchEvent()否则使用默认的dispatchTouchEvent()
//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);
}
如果是Activity,在attach()方法中,注册了Callback:
//Activity.java
final void attach(){
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setCallback(this);//Activity实现了Window.Callback接口
}
所以 DecorView.dispatchTouchEvent() 应当来到Activity的dispatchTouchEvent():
//Activity.java
//实现了Window.Callback接口
@Override
public boolean dispatchTouchEvent(MotionEvent ev){
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();//空实现,可重写
}
//getWindow()来到PhoneWindow
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
//如果孩子没有消费这个事件,就会回到Activity的onTouchEvent中
return onTouchEvent(ev);
}
Activity实现的Window.Callback接口中,先有一个钩子函数捕获到down事件,接下来来到PhoneWindow.superDispatchTouchEvent()这个函数直接调用了 DecorView.superDispatchTouchEvent() 也就是以ViewGroup的身份,向下传递事件:
//PhoneWindow.java
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
//DecorView.java
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
接下来,就来到了ViewGroup的dispatchTouchEvent()
2.2 ViewGroup的触摸事件传递
在ViewGroup的dispatchTouchEvent()中,主要做了几件事:
- 检查事件的安全性
- onInterceptTouchEvent()拦截事件
- 如果触摸事件没有取消,且 2 没被拦截,将会遍历所有子View进行分发
- down事件只分发给触摸点在子View范围之内的子View,并记录是哪个View消费了down事件
- 后续通过TouchTarget来分发move、up等事件
//ViewGroup.java
public boolean dispatchTouchEvent(MotionEvent ev) {
//------onInterceptTouchEvent()拦截事件---------
final boolean intercepted = false;
if (actionMasked == MotionEvent.ACTION_DOWN
//如果不是Down,但是mFirstTouchTarget不为空,说明有targetView消费了down,仍然需要尝试拦截
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//如果孩子请求了 disallowParentIntercept,这里可能会进不去
if (!disallowIntercept) {
//先过一层onInterceptTouchEvent
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
intercepted = true;
}
//------分发Down事件,并记录是哪个子View消费的---------
//如果onInterceptTouchEvent()不拦截,而且event没有cancel取消
if(!canceled && ! intercepted){
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
//如果是down事件
//遍历所有子View
final View[] children = mChildren;
//统计所有触摸到的view
for (int i = childrenCount - 1; i >= 0; i--) {
//遍历所有子View
//判断这个触摸点是否在Child中,且这个Child可以接受触摸事件
if (!child.canReceivePointerEvents()
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
//拿到这个子View的TouchTarget(同时也拿到了这个孩子的孩子的TouchTarget,这是一棵树的某条路径)
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
//触摸事件(down事件)分发给这个孩子的dispatchTouchEvent()
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
//如果这个down事件被这个子View消费了,进来到这里(此时它的TouchTarget也被递归更新了)
//由于这个孩子的mTouchTarget在孩子自己的分发过程中递归更新了
//孩子的mTouchTarget.next = 父亲的mFirstTouchTarget
//父亲的mFirstTouchTarget = 孩子的mTouchTarget
//最后这个newTouchTarget的形式就是,头为最后消费者,后缀为消费者View的父亲View
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
ev.setTargetAccessibilityFocus(false);
}
//如果没有孩子处理事件
if (newTouchTarget == null && mFirstTouchTarget != null) {
//遍历找到之前的根View的TouchTarget作为newTouchTarget
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
//除了down的其他事件,后续都通过TouchTarget来分发:
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
}else{
//如果之前有TouchTarget记录,则往其上分发事件
TouchTarget predesessor = null;//前一个TouchTarget
TouchTarget target = mFirstTouchTarget;//这个mFirstTouchTarget是最终处理了Down事件的View,它的next是其父亲View的TouchTarget
while(target != null){
//子View的TouchTarget的next是其父亲的TouchTarget
final TouchTarget next = target.next;
//给孩子分发触摸事件
//后续的例如move事件都在这里分发下去,分发给target
if (dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)) {
handled = true;
}
//同时也把这个事件分发给其父亲的TouchTarget,进入dispatchTouchEvent()
predecessor = target;
target = next;
}
}
//返回是否被消费
return handled;
}
这里出现了TouchTarget,这是ViewGroup的内部类,这是一个链表数据结构,用于记录在View树上,触摸事件传递的路径(传递经过了哪些View):
//ViewGroup.java
private static final class TouchTarget {
private static TouchTarget sRecycleBin;//复用池
public View child;//聚合到的View
public TouchTarget next;//链表
}
此外,分发事件通过的是 dispatchTransformedTouchEvent() 方法:
//ViewGroup.java
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
//如果是cancel事件
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
//如果没有View进行分发,就使用默认
handled = super.dispatchTouchEvent(event);
} else {
//分发到子View
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
//...
// 分发事件,如move、up等
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
//发给子View
handled = child.dispatchTouchEvent(transformedEvent);
}
}
回到ViewGroup的dispatchTouchEvent的逻辑,再次梳理一下:
- 检查事件的安全性
- onInterceptTouchEvent()拦截事件
- 如果触摸事件没有取消,且 2 没被拦截,将会遍历所有子View进行分发
- down事件只分发给触摸点在子View范围之内的子View,并记录是哪个View消费了down事件
- 后续通过TouchTarget来分发move、up等事件
- 如果2拦截了,那么 intercepted 变量为 true,就不会将 down 事件分发给子View,那么 TouchTarget 这样一个树上传递路径的记录就不会记录到子View,后续非 down 事件的分发就不会传递给子View,实现了拦截
- 如果没有子View进行分发,最后会进入 super.dispatchTouchEvent() 也就来到了父类 View .java的dispatchTouchEvent() 默认实现
2.3 View的触摸事件传递
如果某个ViewGroup拦截了触摸事件,会来到父类View.java实现的dispatchTouchEvent()中。也有可能事件传递给了子View,本就是View类型,直接来到dispatchTouchEvent()中。
View.java的dispatchTouchEvent()主要做了几件事:
- 触摸事件的安全检查
- mOnTouchListener.onTouch(),如果有监听器,优先回调监听器的onTouch()方法
- 如果 2 消费了事件,就不会再进入 onTouchEvent()
//View.java
public boolean dispatchTouchEvent(MotionEvent event) {
boolean result = false;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
//先调用TouchListener的onTouch
//如果消费了,return true,那么 result= true
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//如果OnTouchListener.onTouch消费了事件,result=true就不会进入到onTouchEvent()中
//onTouchEvent()是默认实现,用户可以重写onTouchEvent,或者添加OnTouchListener来处理自己的逻辑。OnTouchLIstener优先级更高
if (!result && onTouchEvent(event)) {
result = true;
}
//...
return result;
}
其中,onTouchEvent()的默认实现中,有click事件与长按事件:
//View.java
public boolean onTouchEvent(MotionEvent event) {
//默认处理逻辑
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
//当前控件是否可点击
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
//如果当前来到的手势是 ACTION_UP
case MotionEvent.ACTION_UP:
//一系列判断
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
//点击事件,使用post,而不是直接回调
if (!post(mPerformClick)) {
performClickInternal();
}
//...
return true;
}
return false;
}
也就是说,如果某个ViewGroup拦截了事件,或者它的onTouchEvent消费了事件,后续都将通过其super.dispatchTouchEvent()也就是View.java的dispatchTouchEvent()来到listener.onTouch()或者onTouchEvent()处理事件。