驅(qū)動(dòng)
-> 觸摸屏幕出發(fā)硬件驅(qū)動(dòng)
-> 產(chǎn)生原生態(tài)的內(nèi)核事件
-> Linux內(nèi)核講事件包裝為通用的Event存到/dev/input/event[x]目錄下
SystemServer
當(dāng)系統(tǒng)啟動(dòng)時(shí),SystemServer進(jìn)程會(huì)啟動(dòng)一些列的系統(tǒng)服務(wù),如AMS,WMS等,包括InputManagerService。
InputManagerService
作用:與硬件通信,接收屏幕輸入事件。
內(nèi)部啟動(dòng)了一個(gè)InputReader讀線程,從到/dev/input/目錄拿到事件
InputReader將事件分發(fā)給InputDispatcher線程,進(jìn)行統(tǒng)一的事件分發(fā)調(diào)度
IMS通過(guò)InputChannel進(jìn)程間通訊(socket)將事件傳遞給應(yīng)用進(jìn)程
InputChannel在ViewRootImpl.setView時(shí),進(jìn)行了注冊(cè)
當(dāng)應(yīng)用進(jìn)程接收到消息,通過(guò)native回調(diào)到InputEventReceiver.dispachInputEvent -> ViewRootImpl
當(dāng)時(shí)間分發(fā)完成后,會(huì)調(diào)用finishInputEvent,告知InputDispatcher將事件移除,完成本次事件分發(fā)
InputEventReceiver
// 由native調(diào)用
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
// 這里ViewRootImpl中注冊(cè)了一個(gè)WindowInputEventReceiver
onInputEvent(event);
}
ViewRootImpl
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
mView = view;
// 屏幕輸入事件
mInputChannel = new InputChannel();
...
requestLayout();
// 添加window時(shí),注冊(cè)mInputChannel
mWindowSession.addToDisplayAsUser(mWindow, ...., mInputChannel, ...);
// 注冊(cè)輸入輸入事件回調(diào)
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
Looper.myLooper());
...
view.assignParent(this);
...
}
// 最終調(diào)用到ViewPostImeInputStage 中的 mView.dispatchTouchEvent(event)
// 這里的mView為DecorView
// 責(zé)任鏈模式,最終調(diào)用到ViewPostImeInputStage 中的 mView.dispatchPointerEvent
// 內(nèi)部調(diào)用了 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) {
....
// 這個(gè)方法最終調(diào)用到deliverInputEvent
enqueueInputEvent(event, this, 0, true);
}
}
// 責(zé)任鏈模式
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
// 調(diào)用到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);
}
// 調(diào)用父類(lèi)即使ViewGroup的dispatchTouchEvent往下分發(fā)事件
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
Activity
public boolean dispatchTouchEvent(MotionEvent ev) {
...
// 調(diào)用PhoneWindow的superDispatchTouchEvent
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
PhoneWindow
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
// 調(diào)用DecorView
return mDecor.superDispatchTouchEvent(event);
}
ViewGroup
// 第一個(gè)觸摸目標(biāo),用于判斷是否有childView消費(fèi)事件,鏈表結(jié)構(gòu)
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通過(guò)調(diào)用ParentView.requestDisallowInterceptTouchEvent設(shè)置
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
// 允許攔截
if (!disallowIntercept) {
// 調(diào)用自身的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事件會(huì)判斷一下是否有子View產(chǎn)生消費(fèi),初始化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;
}
}
}
}
// 沒(méi)有相應(yīng)的觸摸目標(biāo)
if (mFirstTouchTarget == null) {
// 當(dāng)作普通的view進(jìn)行分發(fā)
handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
} else {
// 分發(fā)到觸摸目標(biāo)(整個(gè)鏈表)
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
// 如果已經(jīng)分發(fā)過(guò),則無(wú)需再次分發(fā),排除DOWN事件已經(jīng)分發(fā)過(guò)的事件
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = intercepted || ...;
// 向整個(gè)觸摸目標(biāo)鏈表分發(fā)事件,如果ViewGroup攔截了事件,則分發(fā)取消事件
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
// 如果ViewGroup攔截了事件,當(dāng)遍歷完觸摸目標(biāo)后,mFirstTouchTarget為null
if (cancelChild) {
...
mFirstTouchTarget = next;
continue;
}
}
...
target = next;
}
}
return handled;
}
// childView事件分發(fā)
// 將運(yùn)動(dòng)事件轉(zhuǎn)換為特定子視圖的坐標(biāo)空間,過(guò)濾掉不相關(guān)的指針 ID,并在必要時(shí)覆蓋其操作。 如果 child 為 null,則 MotionEvent 將改為發(fā)送到此 ViewGroup。
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
// 分發(fā)取消的事件
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;
}
// 過(guò)濾不相關(guān)的指針
// 如果由于某種原因我們最終處于不一致的狀態(tài),看起來(lái)我們可能會(huì)產(chǎn)生一個(gè)沒(méi)有指針的運(yùn)動(dòng)事件,然后刪除該事件。
if (newPointerIdBits == 0) {
return false;
}
...
// 一些變換操作
final MotionEvent transformedEvent = ...
if (child == null) {
// 沒(méi)有子View消費(fèi)事件,則將事件傳遞給父類(lèi),其中會(huì)調(diào)用到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響應(yīng)事件,并傳遞到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監(jiān)聽(tīng),先于OnClickListener,如果返回true,則OnClickListener不會(huì)響應(yīng)
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
// View的onTouchEvent處理,及Click監(jiān)聽(tīng)
if (!result && onTouchEvent(event)) {
result = true;
}
return result;
}
Click
// View的onTouchEvent處理,及Click監(jiān)聽(tīng)
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狀態(tài)
if ((viewFlags & ENABLED_MASK) == DISABLED) {
...
return clickable;
}
if (clickable){
switch (action) {
case MotionEvent.ACTION_UP:
...
// 點(diǎn)擊處理及回調(diào)
performClickInternal();
break;
...
}
return true;
}
return false;
}
一個(gè)事件完整流程
-> 驅(qū)動(dòng)
-> IMS
-> InputEventReceiver
-> ViewRootImpl
-> DecorView(dispatchInputEvent)
-> Activity(dispatchInputEvent)
-> PhoneWindow(superDispatchTouchEvent)
-> DecorView(父類(lèi)ViewGroup的dispatchInputEvent)
-> 分發(fā)給子View
-> 回到ViewRootImpl,結(jié)束事件分發(fā)
文字描述:
1.驅(qū)動(dòng)響應(yīng)事件并包裝為Event
2.通過(guò)InputManagerService將Event傳遞到應(yīng)用進(jìn)程
3.通過(guò)native回調(diào)到InputEventReceiver
4.ViewRootImpl注冊(cè)了InputChannel及InputEventReceiver,所以可以接收到事件,
收到事件后,傳遞給DecorView.dispatchInputEvent,這里使用到了責(zé)任鏈模式
5.DecorView調(diào)用Activity.dispatchInputEvent
6.Activity調(diào)用PhoneWindow.superDispatchTouchEvent
7.PhoneWindow調(diào)用DecorView.superDispatchTouchEvent
8.DecorView調(diào)用父類(lèi)super.dispatchInputEvent
9.當(dāng)沒(méi)有子View消費(fèi),會(huì)調(diào)用Activity.onTouchEvent()
10.不管有沒(méi)有子View消費(fèi),都會(huì)回到ViewRootImp,然后將事件移除,結(jié)束這次事件分發(fā)
示例
RecyclerView的手勢(shì)Down、Move、Up的分發(fā),item的點(diǎn)擊事件分發(fā)邏輯
Down
1.RecyclerView收到Down事件,由于子View沒(méi)有請(qǐng)求攔截事件(disallowIntercept為false),所以會(huì)判斷是否由自身攔截事件(onInterceptTouchEvent)
2.由于Down事件沒(méi)有觸發(fā)RecyclerView的滾動(dòng),所以沒(méi)有攔截(onInterceptTouchEvent返回false),事件會(huì)繼續(xù)往下分發(fā)到子View(dispatchTouchEvent)
3.子View的dispatchTouchEvent會(huì)將事件流轉(zhuǎn)到onTouchEvent,由于子View設(shè)置了點(diǎn)擊事件,所以onTouchEvent返回true表示進(jìn)行消費(fèi)
4.RecyclerView的Down事件分發(fā),有子View產(chǎn)生消費(fèi),所以mFirstTouchTarget不為null
Move
1.RecyclerView收到Move事件,由于mFirstTouchTarget不為null,所以會(huì)繼續(xù)判斷是否由自身攔截事件(onInterceptTouchEvent)
2.如果onInterceptTouchEvent判斷產(chǎn)生了滾動(dòng),則返回true表示要進(jìn)行攔截,會(huì)向mFirstTouchTarget分發(fā)一個(gè)Cancel事件后,將mFirstTouchTarget置為null
3.由于mFirstTouchTarget為null,所以后續(xù)的Move事件都會(huì)被RecyclerView攔截掉,不會(huì)再往下分發(fā),會(huì)將事件分發(fā)給自身的onTouchEvent
Up
1.如果在Move中,RecyclerView產(chǎn)生滾動(dòng)攔截了事件,則后續(xù)Move、Up都不會(huì)傳遞到子View
2.RecyclerView收到Up事件,并且未產(chǎn)生滾動(dòng)事件,會(huì)將事件繼續(xù)分發(fā)到子View(同Down),
3.子View收到Up事件后,onTouchEvent判斷產(chǎn)生了點(diǎn)擊事件后,回調(diào)OnClick