您好,登錄后才能下訂單哦!
在InputReader從EventHub中獲取輸入事件,包含觸摸屏事件、物理按鍵事件等,然后轉(zhuǎn)交給InputDispatcher線程,InputDispatcher經(jīng)過篩選,過濾輸入事件。對于觸摸事件通過調(diào)用findTouchedWindowTargetsLocked()函數(shù)找到合適的InputTarget,然后通過dispatchEventLocked()->prepareDispatchCycleLocked()->enqueueDispatchEntriesLocked()->enqueueDispatchEntryLocked()-> connection->outboundQueue.enqueueAtTail(dispatchEntry)添加到與InputTarget一一對應(yīng)的connection中的一個隊列中。如果之前該隊列無數(shù)據(jù),并且當(dāng)前觸摸事件已成功加入該隊列,則繼續(xù)調(diào)用startDispatchCycleLocked()函數(shù)進行分發(fā)處理。在startDispatchCycleLocked()中,有一個while循環(huán),該循環(huán)從connection->outboundQueue隊列中取出輸入事件,如果該輸入事件是按鍵(key)事件,則調(diào)用connection->inputPublisher.publishKeyEvent()函數(shù),如果是觸摸事件則調(diào)用connection->inputPublisher.publishMotionEvent()。publishKeyEvent()和publishMotionEvent()都是調(diào)用mChannel->sendMessage()將輸入事件發(fā)送出去。mChannel是一個C++層InputChannel對象,該對象的賦值過程如下:registerInputChannel()->new Connection->Connection()構(gòu)造函數(shù)->InputPublisher()構(gòu)造函數(shù)。事實上,在registerInputChannel()被調(diào)用之前,ViewRootImple在增加一個窗口時調(diào)用ViewRootImpl.setView()->mWindowSession.addToDisplay()-WindowManagerService.addWindow(),在addWindow()中會創(chuàng)建一對InputChannel(Nativie層),實際上是創(chuàng)建一對Socket,服務(wù)端InputChanel被WMS注冊到InputDispatcher中,客戶端InputChannel被返回給ViewRootImpl,ViewRootImpl將客戶端InputChannel作為參數(shù)new一個InputEventReceiver對象,在InputEventReceiver()構(gòu)造函數(shù)中繼續(xù)調(diào)用nativeInit()函數(shù)來創(chuàng)建一個native層的NativeInputEventReceiver對象,前面創(chuàng)建的客戶端InputChannel會保存在該對象中。
總結(jié):WMS會調(diào)用native層接口創(chuàng)建一對套接字,服務(wù)端保存在InputDispatcher中,客戶端保存在NativeInputEventReceiver中(android_view_inputEventReceiver.cpp)。
很容易想到輸入事件是從InputDispatcher流向NativeInputEventReceiver中。在創(chuàng)建一個native層的NativeInputEventReceiver對象后會立即調(diào)用NativeInputEventReceiver->initialize(),該函數(shù)調(diào)用mMessageQueue->getLooper()->addFd(fd,0, events, this, NULL)將客戶端socket句柄添加到Looper的輪詢隊列中,參數(shù)this指向NativeInputEventReceiver本身,意味著只要服務(wù)端InputDispatcher發(fā)送輸入事件,客戶端收到這個事件,就調(diào)用NativeInputEventReceiver的某個函數(shù),具體調(diào)用哪個函數(shù),自然是NativeInputEventReceiver實現(xiàn)了LooperCallback的接口函數(shù)handleEvent()。但此時收到的事件只是代表socket客戶端有事件來,并沒有把具體的事件讀取出來,這點需要注意。
總結(jié):客戶端收到輸入事件,即調(diào)用NativeInputEventReceiver->handleEvent()函數(shù)。
在handleEvent()函數(shù)中,繼續(xù)調(diào)用consumeEvents()->mInputConsumer.consume()->mChannel->receiveMessage(&mMsg)將具體輸入事件讀取出來,然后調(diào)用env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.dispatchInputEvent,seq, inputEventObj),可以知道native層讀取輸入事件后,然后會回調(diào)java層InputEventReceiver.java中的dispatchInputEvent()函數(shù)。事實上,
dispatchInputEvent繼續(xù)調(diào)用onInputEvent(event); 此時可能并不調(diào)用InputEventReceiver類中的onInputEvent()方法,而是調(diào)用子類onInputEvent()方法。在 ViewRootImpl中存在WindowInputEventReceiver類型變量 mInputEventReceiver,WindowInputEventReceiver類繼承InputEventReceiver,并實現(xiàn) onInputEvent()方法由此可得出結(jié)論:native層socket客戶端讀取輸入事件,最終調(diào)用InputEventReceiver類子類 的onInputEvent()方法,ViewRootImpl繼承InputEventReceiver,因此 ViewRootImpl.onInputEvent()將被調(diào)用。
總結(jié):對于一般的觸摸屏事件最終處理者是ViewRootImpl類,對于輸入法則處理者是IInputMethodSessionWrapper類,當(dāng)然WMS是不會處理這些輸入事件的。
繼 續(xù)研究ViewRootImpl.onInputEvent()函 數(shù),onInputEvent()->doProcessInputEvents()->deliverInputEvent(),deliverInputEvent() 函數(shù)中會調(diào)用stage.deliver(q),stage是mFirstPostImeInputStage 或 mFirstInputStage,這個兩個InputStage對象在setView中賦值。InputStage類設(shè)計就是責(zé)任鏈模式。因為觸摸事件是要分發(fā)到具體的View上來,所以對于一般的觸摸事件最后是傳遞到ViewPostImeInputStage類中來處理,處理函數(shù)是processPointerEvent(q),這個函數(shù)調(diào)用mView.dispatchPointerEvent(event)將事件分發(fā)出去,mView具體是什么呢?mView其實就是DecorView,每一個窗口有且僅有一個DecorView,且處在最頂層,由于DecorView未重寫dispatchPointerEvent(),所以調(diào)用還是父類View類的dispatchPointerEvent()方法。
[java] view plaincopy
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
該方法繼續(xù)調(diào)用dispatchTouchEvent(event),DecorView重新了該方法:
[java] view plaincopy
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Callback cb = getCallback();
return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
: super.dispatchTouchEvent(ev);
}
getCallback()
函數(shù)獲取apk注冊的用于攔截按鍵、觸摸等事件的回調(diào)函數(shù)。一般window不會攔截處理觸摸事件,所以會繼續(xù)調(diào)用
super.dispatchTouchEvent(ev),即父類ViewGroup的dispatchTouchEvent()函數(shù),在該函數(shù)中尋找
到對應(yīng)的View再繼續(xù)調(diào)用dispatchTransformedTouchEvent()
[java] view plaincopy
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder ?
getChildDrawingOrder(childrenCount, i) : i;
final View child = children[childIndex];
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
continue;
}
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// Child is already receiving touch within its bounds.
// Give it the new pointer in addition to the ones it is handling.
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
mLastTouchDownIndex = childIndex;
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
具體的分發(fā)規(guī)則可自行研究代碼。
ViewGroup.dispatchTouchEvent()函數(shù)分析
[html] view plaincopy
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
if (DBG_MOTION || DBG_TOUCH) {
Xlog.d(TAG, "(ViewGroup)dispatchTouchEvent 1: ev = " + ev + ",mFirstTouchTarget = "
+ mFirstTouchTarget + ",this = " + this);
}
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
/// M : add log to help debugging
if (intercepted == true) {
if (DBG_TOUCH) {
Xlog.d(TAG, "Touch event was intercepted event = " + ev + ",this = " + this);
}
}
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
// Check for cancelation.
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// Update list of touch targets for pointer down, if needed.
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
if (DBG_MOTION) {
Xlog.d(TAG, "(ViewGroup)dispatchTouchEvent 2: actionMasked = " + actionMasked
+ ",intercepted = " + intercepted + ",canceled = " + canceled + ",split = "
+ split + ",mChildrenCount = " + mChildrenCount + ",mFirstTouchTarget = "
+ mFirstTouchTarget + ",this = " + this);
}
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
// Clean up earlier touch targets for this pointer id in case they
// have become out of sync.
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
final View[] children = mChildren;
final boolean customOrder = isChildrenDrawingOrderEnabled();
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder ?
getChildDrawingOrder(childrenCount, i) : i;
final View child = children[childIndex];
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
if (DBG_MOTION) {
Xlog.d(TAG, "(ViewGroup)dispatchTouchEvent continue 6: i = "
+ i + ",count = " + childrenCount + ",child = " + child
+ ",this = " + this);
}
continue;
}
newTouchTarget = getTouchTarget(child);
if (DBG_MOTION) {
Xlog.d(TAG, "(ViewGroup)dispatchTouchEvent to child 3: child = "
+ child + ",childrenCount = " + childrenCount + ",i = " + i
+ ",newTouchTarget = " + newTouchTarget + ",idBitsToAssign = "
+ idBitsToAssign + ",mFirstTouchTarget = " + mFirstTouchTarget
+ ",this = " + this);
}
if (newTouchTarget != null) {
// Child is already receiving touch within its bounds.
// Give it the new pointer in addition to the ones it is handling.
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
mLastTouchDownIndex = childIndex;
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
}
if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added target.
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
// Dispatch to touch targets.
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it. Cancel touch targets if necessary.
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (DBG_MOTION) {
Xlog.d(TAG, "dispatchTouchEvent middle 5: cancelChild = " + cancelChild
+ ",mFirstTouchTarget = " + mFirstTouchTarget + ",target = "
+ target + ",predecessor = " + predecessor + ",next = " + next
+ ",this = " + this);
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
// Update list of touch targets for pointer up or cancel, if needed.
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
resetTouchState();
} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
final int actionIndex = ev.getActionIndex();
final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
removePointersFromTouchTargets(idBitsToRemove);
}
}
if (DBG_MOTION) {
Xlog.d(TAG, "(ViewGroup)dispatchTouchEvent end 4: handled = " + handled + ",mFirstTouchTarget = "
+ mFirstTouchTarget + ",this = " + this);
}
if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
}
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。