Android4.4之Input模組筆記,android4.4input
在InputReader從EventHub中擷取輸入事件,包含觸控螢幕事件、物理按鍵事件等,然後轉交給InputDispatcher線程,InputDispatcher經過篩選,過濾輸入事件。對於觸摸事件通過調用findTouchedWindowTargetsLocked()函數找到合適的InputTarget,然後通過dispatchEventLocked()->prepareDispatchCycleLocked()->enqueueDispatchEntriesLocked()->enqueueDispatchEntryLocked()-> connection->outboundQueue.enqueueAtTail(dispatchEntry)添加到與InputTarget一一對應的connection中的一個隊列中。如果之前該隊列無資料,並且當前觸摸事件已成功加入該隊列,則繼續調用startDispatchCycleLocked()函數進行分發處理。在startDispatchCycleLocked()中,有一個while迴圈,該迴圈從connection->outboundQueue隊列中取出輸入事件,如果該輸入事件是按鍵(key)事件,則調用connection->inputPublisher.publishKeyEvent()函數,如果是觸摸事件則調用connection->inputPublisher.publishMotionEvent()。publishKeyEvent()和publishMotionEvent()都是調用mChannel->sendMessage()將輸入事件發送出去。mChannel是一個C++層InputChannel對象,該對象的賦值過程如下:registerInputChannel()->new Connection->Connection()建構函式->InputPublisher()建構函式。事實上,在registerInputChannel()被調用之前,ViewRootImple在增加一個視窗時調用ViewRootImpl.setView()->mWindowSession.addToDisplay()-WindowManagerService.addWindow(),在addWindow()中會建立一對InputChannel(Nativie層),實際上是建立一對Socket,服務端InputChanel被WMS註冊到InputDispatcher中,用戶端InputChannel被返回給ViewRootImpl,ViewRootImpl將用戶端InputChannel作為參數new一個InputEventReceiver對象,在InputEventReceiver()建構函式中繼續調用nativeInit()函數來建立一個native層的NativeInputEventReceiver對象,前面建立的用戶端InputChannel會儲存在該對象中。
總結:WMS會調用native層介面建立一對通訊端,服務端儲存在InputDispatcher中,用戶端儲存在NativeInputEventReceiver中(android_view_inputEventReceiver.cpp)。
很容易想到輸入事件是從InputDispatcher流向NativeInputEventReceiver中。在建立一個native層的NativeInputEventReceiver對象後會立即調用NativeInputEventReceiver->initialize(),該函數調用mMessageQueue->getLooper()->addFd(fd,0, events, this, NULL)將用戶端socket控制代碼添加到Looper的輪詢隊列中,參數this指向NativeInputEventReceiver本身,意味著只要服務端InputDispatcher發送輸入事件,用戶端收到這個事件,就調用NativeInputEventReceiver的某個函數,具體調用哪個函數,自然是NativeInputEventReceiver實現了LooperCallback的介面函數handleEvent()。但此時收到的事件只是代表socket用戶端有事件來,並沒有把具體的事件讀取出來,這點需要注意。
總結:用戶端收到輸入事件,即調用NativeInputEventReceiver->handleEvent()函數。
在handleEvent()函數中,繼續調用consumeEvents()->mInputConsumer.consume()->mChannel->receiveMessage(&mMsg)將具體輸入事件讀取出來,然後調用env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.dispatchInputEvent,seq, inputEventObj),可以知道native層讀取輸入事件後,然後會回調java層InputEventReceiver.java中的dispatchInputEvent()函數。事實上,
dispatchInputEvent繼續調用onInputEvent(event);此時可能並不調用InputEventReceiver類中的onInputEvent()方法,而是調用子類onInputEvent()方法。在ViewRootImpl中存在WindowInputEventReceiver類型變數mInputEventReceiver,WindowInputEventReceiver類繼承InputEventReceiver,並實現onInputEvent()方法由此可得出結論:native層socket用戶端讀取輸入事件,最終調用InputEventReceiver類子類的onInputEvent()方法,ViewRootImpl繼承InputEventReceiver,因此ViewRootImpl.onInputEvent()將被調用。
總結:對於一般的觸控螢幕事件最終處理者是ViewRootImpl類,對於IME則處理者是IInputMethodSessionWrapper類,當然WMS是不會處理這些輸入事件的。
繼續研究ViewRootImpl.onInputEvent()函數,onInputEvent()->doProcessInputEvents()->deliverInputEvent(),deliverInputEvent()函數中會調用stage.deliver(q),stage是mFirstPostImeInputStage 或 mFirstInputStage,這個兩個InputStage對象在setView中賦值。InputStage類設計就是責任鏈模式。因為觸摸事件是要分發到具體的View上來,所以對於一般的觸摸事件最後是傳遞到ViewPostImeInputStage類中來處理,處理函數是processPointerEvent(q),這個函數調用mView.dispatchPointerEvent(event)將事件分發出去,mView具體是什麼呢?mView其實就是DecorView,每一個視窗有且僅有一個DecorView,且處在最頂層,由於DecorView未重寫dispatchPointerEvent(),所以調用還是父類View類的dispatchPointerEvent()方法。
public final boolean dispatchPointerEvent(MotionEvent event) { if (event.isTouchEvent()) { return dispatchTouchEvent(event); } else { return dispatchGenericMotionEvent(event); } }該方法繼續調用dispatchTouchEvent(event),DecorView重新了該方法:
<span style="font-family:Microsoft YaHei;font-size:18px;">@Override public boolean dispatchTouchEvent(MotionEvent ev) { final Callback cb = getCallback(); return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev); }</span>
getCallback()函數擷取apk註冊的用於攔截按鍵、觸摸等事件的回呼函數。一般window不會攔截處理觸摸事件,所以會繼續調用super.dispatchTouchEvent(ev),即父類ViewGroup的dispatchTouchEvent()函數,在該函數中尋找到對應的View再繼續調用dispatchTransformedTouchEvent()
<span style="font-family:Microsoft YaHei;font-size:18px;"> 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; } }</span>具體的分發規則可自行研究代碼。
android開發所有的環境搭建完成,為何AVD中的target只有android44不可以選其他的
開啟你的sdkManage,把其他版本的sdk也下載下來就好了.估計你當時就只下載了4.4的
android開發中input type問題
本來就支援加號或減號