驱动
-> 触摸屏幕出发硬件驱动
-> 产生原生态的内核事件
-> Linux内核讲事件包装为通用的Event存到/dev/input/event[x]目录下
SystemServer
当系统启动时,SystemServer进程会启动一些列的系统服务,如AMS,WMS等,包括InputManagerService。
InputManagerService
作用:与硬件通信,接收屏幕输入事件。
内部启动了一个InputReader读线程,从到/dev/input/目录拿到事件
InputReader将事件分发给InputDispatcher线程,进行统一的事件分发调度
IMS通过InputChannel进程间通讯(socket)将事件传递给应用进程
InputChannel在ViewRootImpl.setView时,进行了注册
当应用进程接收到消息,通过native回调到InputEventReceiver.dispachInputEvent -> ViewRootImpl
当时间分发完成后,会调用finishInputEvent,告知InputDispatcher将事件移除,完成本次事件分发
InputEventReceiver
// 由native调用
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
// 这里ViewRootImpl中注册了一个WindowInputEventReceiver
onInputEvent(event);
}
ViewRootImpl
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
mView = view;
// 屏幕输入事件
mInputChannel = new InputChannel();
...
requestLayout();
// 添加window时,注册mInputChannel
mWindowSession.addToDisplayAsUser(mWindow, ...., mInputChannel, ...);
// 注册输入输入事件回调
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
Looper.myLooper());
...
view.assignParent(this);
...
}
// 最终调用到ViewPostImeInputStage 中的 mView.dispatchTouchEvent(event)
// 这里的mView为DecorView
// 责任链模式,最终调用到ViewPostImeInputStage 中的 mView.dispatchPointerEvent
// 内部调用了 mView.dispatchTouchEvent(event)
// 这里的mView为DecorView
private void deliverInputEvent(QueuedInputEvent q) {
InputStage stage;
....
// stage赋值操作
....
if (stage != null) {
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event) {
....
// 这个方法最终调用到deliverInputEvent
enqueueInputEvent(event, this, 0, true);
}
}
// 责任链模式
abstract class InputStage {
private final InputStage mNext;
public InputStage(InputStage next) {
mNext = next;
}
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);
}
}
}
DecorView
// 调用到activity的dispatchTouchEvent
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Window.Callback cb = mWindow.getCallback();
// 这里的mWindow为PhoneWindow,cb为Activity
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
// 调用父类即使ViewGroup的dispatchTouchEvent往下分发事件
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
Activity
public boolean dispatchTouchEvent(MotionEvent ev) {
...
// 调用PhoneWindow的superDispatchTouchEvent
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
PhoneWindow
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
// 调用DecorView
return mDecor.superDispatchTouchEvent(event);
}
ViewGroup
// 第一个触摸目标,用于判断是否有childView消费事件,链表结构
private TouchTarget mFirstTouchTarget;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// 是否需要拦截
final boolean intercepted;
// ACTION_DOWN 或者 mFirstTouchTarget不为空,判断一下是否需要拦截
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
// 判断是否允许拦截,childView通过调用ParentView.requestDisallowInterceptTouchEvent设置
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
// 允许拦截
if (!disallowIntercept) {
// 调用自身的onInterceptTouchEvent判断是否需要拦截
intercepted = onInterceptTouchEvent(ev);
...
} else {
// 不允许拦截
intercepted = false;
}
} else {
// 如果mFirstTouchTarget为null且不是ACTION_DOWN,
intercepted = true;
}
final boolean canceled = ...;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
// 不拦截
if (!canceled && !intercepted) {
...
// Down事件会判断一下是否有子View产生消费,初始化mFirstTouchTarget
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
...
for (int i = childrenCount - 1; i >= 0; i--) {
final View child = ...
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// 添加mFirstTouchTarget链表中
newTouchTarget = addTouchTarget(child, idBitsToAssign)
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
}
}
// 没有相应的触摸目标
if (mFirstTouchTarget == null) {
// 当作普通的view进行分发
handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
} else {
// 分发到触摸目标(整个链表)
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
// 如果已经分发过,则无需再次分发,排除DOWN事件已经分发过的事件
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = intercepted || ...;
// 向整个触摸目标链表分发事件,如果ViewGroup拦截了事件,则分发取消事件
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
// 如果ViewGroup拦截了事件,当遍历完触摸目标后,mFirstTouchTarget为null
if (cancelChild) {
...
mFirstTouchTarget = next;
continue;
}
}
...
target = next;
}
}
return handled;
}
// childView事件分发
// 将运动事件转换为特定子视图的坐标空间,过滤掉不相关的指针 ID,并在必要时覆盖其操作。 如果 child 为 null,则 MotionEvent 将改为发送到此 ViewGroup。
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
// 分发取消的事件
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
// 过滤不相关的指针
// 如果由于某种原因我们最终处于不一致的状态,看起来我们可能会产生一个没有指针的运动事件,然后删除该事件。
if (newPointerIdBits == 0) {
return false;
}
...
// 一些变换操作
final MotionEvent transformedEvent = ...
if (child == null) {
// 没有子View消费事件,则将事件传递给父类,其中会调用到onTouchEvent
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
handled = child.dispatchTouchEvent(transformedEvent);
}
return handled;
}
// 添加mFirstTouchTarget
private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
target.next = mFirstTouchTarget;
mFirstTouchTarget = target;
return target;
}
View
// View响应事件,并传递到onTouchEvent方法中
public boolean dispatchTouchEvent(MotionEvent event) {
boolean result = false;
...
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
stopNestedScroll();
}
...
ListenerInfo li = mListenerInfo;
// View的OnTouchListener监听,先于OnClickListener,如果返回true,则OnClickListener不会响应
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
// View的onTouchEvent处理,及Click监听
if (!result && onTouchEvent(event)) {
result = true;
}
return result;
}
Click
// View的onTouchEvent处理,及Click监听
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 = ...;
// Disabled状态
if ((viewFlags & ENABLED_MASK) == DISABLED) {
...
return clickable;
}
if (clickable){
switch (action) {
case MotionEvent.ACTION_UP:
...
// 点击处理及回调
performClickInternal();
break;
...
}
return true;
}
return false;
}
一个事件完整流程
-> 驱动
-> IMS
-> InputEventReceiver
-> ViewRootImpl
-> DecorView(dispatchInputEvent)
-> Activity(dispatchInputEvent)
-> PhoneWindow(superDispatchTouchEvent)
-> DecorView(父类ViewGroup的dispatchInputEvent)
-> 分发给子View
-> 回到ViewRootImpl,结束事件分发
文字描述:
1.驱动响应事件并包装为Event
2.通过InputManagerService将Event传递到应用进程
3.通过native回调到InputEventReceiver
4.ViewRootImpl注册了InputChannel及InputEventReceiver,所以可以接收到事件,
收到事件后,传递给DecorView.dispatchInputEvent,这里使用到了责任链模式
5.DecorView调用Activity.dispatchInputEvent
6.Activity调用PhoneWindow.superDispatchTouchEvent
7.PhoneWindow调用DecorView.superDispatchTouchEvent
8.DecorView调用父类super.dispatchInputEvent
9.当没有子View消费,会调用Activity.onTouchEvent()
10.不管有没有子View消费,都会回到ViewRootImp,然后将事件移除,结束这次事件分发
示例
RecyclerView的手势Down、Move、Up的分发,item的点击事件分发逻辑
Down
1.RecyclerView收到Down事件,由于子View没有请求拦截事件(disallowIntercept为false),所以会判断是否由自身拦截事件(onInterceptTouchEvent)
2.由于Down事件没有触发RecyclerView的滚动,所以没有拦截(onInterceptTouchEvent返回false),事件会继续往下分发到子View(dispatchTouchEvent)
3.子View的dispatchTouchEvent会将事件流转到onTouchEvent,由于子View设置了点击事件,所以onTouchEvent返回true表示进行消费
4.RecyclerView的Down事件分发,有子View产生消费,所以mFirstTouchTarget不为null
Move
1.RecyclerView收到Move事件,由于mFirstTouchTarget不为null,所以会继续判断是否由自身拦截事件(onInterceptTouchEvent)
2.如果onInterceptTouchEvent判断产生了滚动,则返回true表示要进行拦截,会向mFirstTouchTarget分发一个Cancel事件后,将mFirstTouchTarget置为null
3.由于mFirstTouchTarget为null,所以后续的Move事件都会被RecyclerView拦截掉,不会再往下分发,会将事件分发给自身的onTouchEvent
Up
1.如果在Move中,RecyclerView产生滚动拦截了事件,则后续Move、Up都不会传递到子View
2.RecyclerView收到Up事件,并且未产生滚动事件,会将事件继续分发到子View(同Down),
3.子View收到Up事件后,onTouchEvent判断产生了点击事件后,回调OnClick
网友评论