Android輸入輸出機制之來龍去脈之前生後世
先講一下基本一般的輸入處理方式的知識。一般的輸入輸出採用生產者,消費者模式,並構造佇列進行處理,如下圖
這種輸入模型在android的系統中很多地方採用,先從最底層說起:
為了由於觸屏事件頻率很高,android設計者講一個迴圈執行緒,拆分為兩級迴圈,並做了個佇列來進行緩衝。
InputDispatcherThread和InputReaderThread
InputDispatcherThread在自己的迴圈中對InputReaderThread請求同步,InputReaderThread收到同步訊號後,把事件放入InputDispatcher的佇列中。
具體程式碼如下:
InputReader.cpp中有很多InputMapper,有SwitchInputMapper,KeyBoardInputMapper,TrackballInputMapper,SingleTouchInputMapper,
MultiTouchInputMapper。當執行緒從EventHub讀取到Event後,呼叫這些InputMapper的pocess方法:
[java] view plaincopyprint?- 檔案InputReader.cpp中:
- bool InputReaderThread::threadLoop() {
- mReader->loopOnce();
- returntrue;
- }
- void InputReader::loopOnce() {
- RawEvent rawEvent;
- mEventHub->getEvent(& rawEvent);
- #if DEBUG_RAW_EVENTS
- LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d",
- rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode,
- rawEvent.value);
- #endif
- process(& rawEvent);
- }
檔案InputReader.cpp中:
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
void InputReader::loopOnce() {
RawEvent rawEvent;
mEventHub->getEvent(& rawEvent);
#if DEBUG_RAW_EVENTS
LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d",
rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode,
rawEvent.value);
#endif
process(& rawEvent);
}
process如下
[java] view plaincopyprint?- void InputReader::process(const RawEvent* rawEvent) {
- switch (rawEvent->type) {
- consumeEvent(rawEvent);
- break;
- }
- }
void InputReader::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
consumeEvent(rawEvent);
break;
}
}
Java程式碼
- consumeEvent(rawEvent);
- consumeEvent(rawEvent);
consumeEvent(rawEvent);
方法是關鍵,下面繼續跟進;
Java程式碼 [java] view plaincopyprint?- void InputReader::consumeEvent(const RawEvent* rawEvent) {
- int32_t deviceId = rawEvent->deviceId;
- {
- device->process(rawEvent);
- } // release device registry reader lock
- }
void InputReader::consumeEvent(const RawEvent* rawEvent) {
int32_t deviceId = rawEvent->deviceId;
{
device->process(rawEvent);
} // release device registry reader lock
}
device->process(rawEvent)行, 跟進去:
Java程式碼 [java] view plaincopyprint?- void InputDevice::process(const RawEvent* rawEvent) {
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
- mapper->process(rawEvent);
- }
- }
void InputDevice::process(const RawEvent* rawEvent) {
size_t numMappers = mMappers.size();
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
mapper->process(rawEvent);
}
}
下面進入了IputMapper,InputMapper是個純虛類,process是個純虛方法,隨便找個例子跟進去:
[cpp] view plaincopyprint?- void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
- switch (rawEvent->type) {
- case EV_KEY:
- switch (rawEvent->scanCode) {
- case BTN_TOUCH:
- mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH;
- mAccumulator.btnTouch = rawEvent->value != 0;
- // Don't sync immediately. Wait until the next SYN_REPORT since we might
- // not have received valid position information yet. This logic assumes that
- // BTN_TOUCH is always followed by SYN_REPORT as part of a complete packet.
- break;
- }
- break;
- case EV_SYN:
- switch (rawEvent->scanCode) {
- case SYN_REPORT:
- sync(rawEvent->when);
- break;
- }
- break;
- }
- }
void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EV_KEY:
switch (rawEvent->scanCode) {
case BTN_TOUCH:
mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH;
mAccumulator.btnTouch = rawEvent->value != 0;
// Don't sync immediately. Wait until the next SYN_REPORT since we might
// not have received valid position information yet. This logic assumes that
// BTN_TOUCH is always followed by SYN_REPORT as part of a complete packet.
break;
}
break;
case EV_SYN:
switch (rawEvent->scanCode) {
case SYN_REPORT:
sync(rawEvent->when);
break;
}
break;
}
}
最關鍵的是
Cpp程式碼- sync(rawEvent->when);
- sync(rawEvent->when);
sync(rawEvent->when);
展開如下:
Cpp程式碼 [cpp] view plaincopyprint?- void SingleTouchInputMapper::sync(nsecs_t when) {
- syncTouch(when, true);
- }
void SingleTouchInputMapper::sync(nsecs_t when) {
syncTouch(when, true);
}
Java程式碼
[java]
view plaincopyprint?
- void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) {
- if (touchResult == DISPATCH_TOUCH) {
- detectGestures(when);
- dispatchTouches(when, policyFlags);
- }
- }
void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) {
if (touchResult == DISPATCH_TOUCH) {
detectGestures(when);
dispatchTouches(when, policyFlags);
}
}
這兩行,一個是虛擬鍵盤,一個是觸控式螢幕。
TouchResult touchResult = consumeOffScreenTouches(when, policyFlags);
Java程式碼- dispatchTouches
-
Cpp程式碼
- void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
- // Dispatch pointer down events using the new pointer locations.
- while (!downIdBits.isEmpty()) {
- dispatchTouch(when, policyFlags, &mCurrentTouch,
- activeIdBits, downId, pointerCount, motionEventAction);
- }
- }
- }
- void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
- // Dispatch pointer down events using the new pointer locations.
- while (!downIdBits.isEmpty()) {
- dispatchTouch(when, policyFlags, &mCurrentTouch,
- activeIdBits, downId, pointerCount, motionEventAction);
- }
- }
- }
Cpp程式碼void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { // Dispatch pointer down events using the new pointer locations. while (!downIdBits.isEmpty()) { dispatchTouch(when, policyFlags, &mCurrentTouch, activeIdBits, downId, pointerCount, motionEventAction); } } }
- dispatchTouch(when, policyFlags, &mCurrentTouch,
- activeIdBits, downId, pointerCount, motionEventAction);
- dispatchTouch(when, policyFlags, &mCurrentTouch,
- activeIdBits, downId, pointerCount, motionEventAction);
dispatchTouch(when, policyFlags, &mCurrentTouch, activeIdBits, downId, pointerCount, motionEventAction);
這個方法展開如下:
Java程式碼- void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags,
- TouchData* touch, BitSet32 idBits, uint32_t changedId, uint32_t pointerCount,
- int32_t motionEventAction) {
- int32_t pointerIds[MAX_POINTERS];
- PointerCoords pointerCoords[MAX_POINTERS];
- int32_t motionEventEdgeFlags = 0;
- float xPrecision, yPrecision;
- {
- getDispatcher()->notifyMotion(when, getDeviceId(), getSources(), policyFlags,
- motionEventAction, 0, getContext()->getGlobalMetaState(), motionEventEdgeFlags,
- pointerCount, pointerIds, pointerCoords,
- xPrecision, yPrecision, mDownTime);
- }
- void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags,
- TouchData* touch, BitSet32 idBits, uint32_t changedId, uint32_t pointerCount,
- int32_t motionEventAction) {
- int32_t pointerIds[MAX_POINTERS];
- PointerCoords pointerCoords[MAX_POINTERS];
- int32_t motionEventEdgeFlags = 0;
- float xPrecision, yPrecision;
- {
- getDispatcher()->notifyMotion(when, getDeviceId(), getSources(), policyFlags,
- motionEventAction, 0, getContext()->getGlobalMetaState(), motionEventEdgeFlags,
- pointerCount, pointerIds, pointerCoords,
- xPrecision, yPrecision, mDownTime);
- }
void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, TouchData* touch, BitSet32 idBits, uint32_t changedId, uint32_t pointerCount, int32_t motionEventAction) { int32_t pointerIds[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; int32_t motionEventEdgeFlags = 0; float xPrecision, yPrecision; { getDispatcher()->notifyMotion(when, getDeviceId(), getSources(), policyFlags, motionEventAction, 0, getContext()->getGlobalMetaState(), motionEventEdgeFlags, pointerCount, pointerIds, pointerCoords, xPrecision, yPrecision, mDownTime); }
這樣就到了InputDiaptcher的notifyMotion方法,這個方法很長,都再處理MOVE事件,將無用的刪除後,留下如下關鍵程式碼:
Java程式碼- void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source,
- uint32_t policyFlags, int32_t action, int32_t flags, int32_t metaState, int32_t edgeFlags,
- uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
- float xPrecision, float yPrecision, nsecs_t downTime) {
- // Just enqueue a new motion event.
- MotionEntry* newEntry = mAllocator.obtainMotionEntry(eventTime,
- deviceId, source, policyFlags, action, flags, metaState, edgeFlags,
- xPrecision, yPrecision, downTime,
- pointerCount, pointerIds, pointerCoords);
- needWake = enqueueInboundEventLocked(newEntry);
- }
- void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source,
- uint32_t policyFlags, int32_t action, int32_t flags, int32_t metaState, int32_t edgeFlags,
- uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
- float xPrecision, float yPrecision, nsecs_t downTime) {
- // Just enqueue a new motion event.
- MotionEntry* newEntry = mAllocator.obtainMotionEntry(eventTime,
- deviceId, source, policyFlags, action, flags, metaState, edgeFlags,
- xPrecision, yPrecision, downTime,
- pointerCount, pointerIds, pointerCoords);
- needWake = enqueueInboundEventLocked(newEntry);
- }
void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, int32_t flags, int32_t metaState, int32_t edgeFlags, uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords, float xPrecision, float yPrecision, nsecs_t downTime) { // Just enqueue a new motion event. MotionEntry* newEntry = mAllocator.obtainMotionEntry(eventTime, deviceId, source, policyFlags, action, flags, metaState, edgeFlags, xPrecision, yPrecision, downTime, pointerCount, pointerIds, pointerCoords); needWake = enqueueInboundEventLocked(newEntry); }
最後一句:
Java程式碼- needWake = enqueueInboundEventLocked(newEntry);
- needWake = enqueueInboundEventLocked(newEntry);
Cpp程式碼needWake = enqueueInboundEventLocked(newEntry);
- bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
- bool needWake = mInboundQueue.isEmpty();
- mInboundQueue.enqueueAtTail(entry);
- switch (entry->type) {
- case EventEntry::TYPE_KEY: {
- KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
- if (isAppSwitchKeyEventLocked(keyEntry)) {
- if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
- mAppSwitchSawKeyDown = true;
- } elseif (keyEntry->action == AKEY_EVENT_ACTION_UP) {
- if (mAppSwitchSawKeyDown) {
- #if DEBUG_APP_SWITCH
- LOGD("App switch is pending!");
- #endif
- mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
- mAppSwitchSawKeyDown = false;
- needWake = true;
- }
- }
- }
- break;
- }
- }
- return needWake;
- }
- bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
- bool needWake = mInboundQueue.isEmpty();
- mInboundQueue.enqueueAtTail(entry);
- switch (entry->type) {
- case EventEntry::TYPE_KEY: {
- KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
- if (isAppSwitchKeyEventLocked(keyEntry)) {
- if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
- mAppSwitchSawKeyDown = true;
- } elseif (keyEntry->action == AKEY_EVENT_ACTION_UP) {
- if (mAppSwitchSawKeyDown) {
- #if DEBUG_APP_SWITCH
- LOGD("App switch is pending!");
- #endif
- mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
- mAppSwitchSawKeyDown = false;
- needWake = true;
- }
- }
- }
- break;
- }
- }
- return needWake;
- }
Cpp程式碼bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { bool needWake = mInboundQueue.isEmpty(); mInboundQueue.enqueueAtTail(entry); switch (entry->type) { case EventEntry::TYPE_KEY: { KeyEntry* keyEntry = static_cast<KeyEntry*>(entry); if (isAppSwitchKeyEventLocked(keyEntry)) { if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) { mAppSwitchSawKeyDown = true; } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) { if (mAppSwitchSawKeyDown) { #if DEBUG_APP_SWITCH LOGD("App switch is pending!"); #endif mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT; mAppSwitchSawKeyDown = false; needWake = true; } } } break; } } return needWake; }
- mInboundQueue正是上面所說的佇列。到此為止,從InputReader插入到佇列就完成了。
- mInboundQueue正是上面所說的佇列。到此為止,從InputReader插入到佇列就完成了。
mInboundQueue正是上面所說的佇列。到此為止,從InputReader插入到佇列就完成了。
那麼InputDispatcher又是如何從佇列中取出來的呢?累了。
InputDiapather的
Cpp程式碼- dispatchOnce
- dispatchOnce
dispatchOnce
方法如下:
Cpp程式碼- void InputDispatcher::dispatchOnce() {
- nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();
- nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay();
- nsecs_t nextWakeupTime = LONG_LONG_MAX;
- { // acquire lock
- AutoMutex _l(mLock);
- dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);
- if (runCommandsLockedInterruptible()) {
- nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
- }
- } // release lock
- // Wait for callback or timeout or wake. (make sure we round up, not down)
- nsecs_t currentTime = now();
- int32_t timeoutMillis;
- if (nextWakeupTime > currentTime) {
- uint64_t timeout = uint64_t(nextWakeupTime - currentTime);
- timeout = (timeout + 999999LL) / 1000000LL;
- timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout);
- } else {
- timeoutMillis = 0;
- }
- mLooper->pollOnce(timeoutMillis);
- }
- void InputDispatcher::dispatchOnce() {
- nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();
- nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay();
- nsecs_t nextWakeupTime = LONG_LONG_MAX;
- { // acquire lock
- AutoMutex _l(mLock);
- dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);
- if (runCommandsLockedInterruptible()) {
- nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
- }
- } // release lock
- // Wait for callback or timeout or wake. (make sure we round up, not down)
- nsecs_t currentTime = now();
- int32_t timeoutMillis;
- if (nextWakeupTime > currentTime) {
- uint64_t timeout = uint64_t(nextWakeupTime - currentTime);
- timeout = (timeout + 999999LL) / 1000000LL;
- timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout);
- } else {
- timeoutMillis = 0;
- }
- mLooper->pollOnce(timeoutMillis);
- }
void InputDispatcher::dispatchOnce() { nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout(); nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay(); nsecs_t nextWakeupTime = LONG_LONG_MAX; { // acquire lock AutoMutex _l(mLock); dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime); if (runCommandsLockedInterruptible()) { nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately } } // release lock // Wait for callback or timeout or wake. (make sure we round up, not down) nsecs_t currentTime = now(); int32_t timeoutMillis; if (nextWakeupTime > currentTime) { uint64_t timeout = uint64_t(nextWakeupTime - currentTime); timeout = (timeout + 999999LL) / 1000000LL; timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout); } else { timeoutMillis = 0; } mLooper->pollOnce(timeoutMillis); }
最關鍵的是
Cpp程式碼- dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);
- dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);
dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);
和
Cpp程式碼- mLooper->pollOnce(timeoutMillis);(這個方法在有個回撥,與事件有關的是【管道裝置的檔案描述符】。
- 具體機制在後面講。這個東西實際上是個跨程序的訊號。傳送給了Android的使用者程序的JNI,又通過JNI呼叫InputQueue的的
- 靜態方法如:dispatchMotionEvent。後面回提到dispatchMotionEvent才是Android使用者程序的事件入口函式。)
- )
- mLooper->pollOnce(timeoutMillis);(這個方法在有個回撥,與事件有關的是【管道裝置的檔案描述符】。
- 具體機制在後面講。這個東西實際上是個跨程序的訊號。傳送給了Android的使用者程序的JNI,又通過JNI呼叫InputQueue的的
- 靜態方法如:dispatchMotionEvent。後面回提到dispatchMotionEvent才是Android使用者程序的事件入口函式。)
- )
mLooper->pollOnce(timeoutMillis);(這個方法在有個回撥,與事件有關的是【管道裝置的檔案描述符】。 具體機制在後面講。這個東西實際上是個跨程序的訊號。傳送給了Android的使用者程序的JNI,又通過JNI呼叫InputQueue的的 靜態方法如:dispatchMotionEvent。後面回提到dispatchMotionEvent才是Android使用者程序的事件入口函式。) )
程式碼又長又臭
Java程式碼- void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
- nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) {
- case EventEntry::TYPE_MOTION: {
- MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
- if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
- dropReason = DROP_REASON_APP_SWITCH;
- }
- done = dispatchMotionLocked(currentTime, typedEntry,
- &dropReason, nextWakeupTime);
- break;
- }
- }
- void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
- nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) {
- case EventEntry::TYPE_MOTION: {
- MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
- if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
- dropReason = DROP_REASON_APP_SWITCH;
- }
- done = dispatchMotionLocked(currentTime, typedEntry,
- &dropReason, nextWakeupTime);
- break;
- }
- }
Java程式碼void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) { case EventEntry::TYPE_MOTION: { MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent); if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) { dropReason = DROP_REASON_APP_SWITCH; } done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); break; } }
- dispatchMotionLocked
- dispatchMotionLocked
dispatchMotionLocked
方法呼叫prepareDispatchCycleLocked,呼叫startDispatchCycleLocked,最終呼叫
// Publish the key event.
status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source,
action, flags, keyEntry->keyCode, keyEntry->scanCode,
keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
keyEntry->eventTime);或者 // Publish the motion event and the first motion sample.
status = co