Step 11. InputDispatcher.dispatchOnceInnerLocked
這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp檔案中:
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) {
......
// Ready to start a new event.
// If we don't already have a pending event, go grab one.
if (! mPendingEvent) {
if (mInboundQueue.isEmpty()) {
......
} else {
// Inbound queue has at least one entry.
EventEntry* entry = mInboundQueue.headSentinel.next;
mInboundQueue.dequeue(entry);
mPendingEvent = entry;
}
......
}
switch (mPendingEvent->type) {
case EventEntry::TYPE_KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
done = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout,
&dropReason, nextWakeupTime);
break;
}
}
我們忽略了這個函數的次要邏輯,主要關注鍵盤事件的主要處理流程。首先,如果前面發生的鍵盤事件都已經處理完畢,那麼這裡的mPendingEvent就為NULL,又因為前面我們把剛剛發生的鍵盤事件加入了mInboundQueue隊列,是以,這裡mInboundQueue不為NULL,于是,這裡就把mInboundQueue隊列中的鍵盤事件取出來,放在mPendingEvent變量中:
mInboundQueue.dequeue(entry);
mPendingEvent = entry;
由于這裡發生的是鍵盤事件,即mPendingEvent->type的值為EventEntry::TYPE_KEY,于是,在接下來的switch語句中就會執行dispatchKeyLocked函數來分發鍵盤消息。
Step 12. InputDispatcher.dispatchKeyLocked
bool InputDispatcher::dispatchKeyLocked(
nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
// Identify targets.
if (! mCurrentInputTargetsValid) {
int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
entry, nextWakeupTime);
// Dispatch the key.
dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
return true;
InputDispatcher類中的mCurrentInputTargetsValid成員變量表示InputDispatcher是否已經标志出誰是目前激活的Activity視窗,如果沒有,就需要通過findFocusedWindowTargetsLocked函數來把它找出來。當把目前激活的Activity視窗找出來以後,接下來就調用dispatchEventToCurrentInputTargetsLocked函數把鍵盤事件分發給它了。
我們先來看一InputDispatcher是如何找到目前激活的Activity視窗的,然後再分析它把鍵盤事件分發給目前激活Activity視窗的過程。
Step 13. InputDispatcher.findFocusedWindowTargetsLocked
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
const EventEntry* entry, nsecs_t* nextWakeupTime) {
mCurrentInputTargets.clear();
int32_t injectionResult;
// If there is no currently focused window and no focused application
// then drop the event.
if (! mFocusedWindow) {
if (mFocusedApplication) {
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplication, NULL, nextWakeupTime);
goto Unresponsive;
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
// Check permissions.
if (! checkInjectionPermission(mFocusedWindow, entry->injectionState)) {
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
// If the currently focused window is paused then keep waiting.
if (mFocusedWindow->paused) {
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplication, mFocusedWindow, nextWakeupTime);
goto Unresponsive;
// If the currently focused window is still working on previous events then keep waiting.
if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindow)) {
// Success! Output targets.
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND, BitSet32(0));
return injectionResult;
回憶前面我們分析應用程式注冊鍵盤消息接收通道的過程時,在Step 9中,目前處于激活狀态的應用程式會通過調用InputDispatcher類setInputWindows函數把把目前獲得焦點的Activity視窗設定到mFocusedWindow中去,是以,這裡的mFocusedWindow不為NULL,于是,就通過了第一個if語句的檢查。
第二個if語句檢查權限問題,原來,這個鍵盤事件除了是由硬體觸發的外,也可以由其它程序注入進來的,如果這個鍵盤事件是由其它程序注入進來的,那麼entry->injectState就不為NULL,它裡面包含了事件注冊者的程序ID和使用者ID,于是,這裡就會調用checkInjectionPermission來檢查這個事件注入者的程序ID和使用者ID,看看它是否具有這個權限。這裡我們不考慮這種情況,是以,這裡的entry->injectState為NULL,于是,這個if語句的檢查也通過了。
第三個if語句檢查目前激活的Activity視窗是否是處于paused狀态,如果是的話,也不用進一步處理了。一般情況下,目前激活的Activity視窗都是處于resumed狀态的,于是,這個if語句的檢查也通過了。
第四個if語句檢查目前激活的Activity視窗是否還正在處理前一個鍵盤事件,如果是的話,那就要等待它處理完前一個鍵盤事件後再來處理新的鍵盤事件了。這裡我們也假設目前激活的Activity視窗不是正在處理前面的鍵盤事件,是以,這個if語句的檢查也通過了。
最後,就調用addWindowTargetLocked函數把目前激活的Activity視窗添加到InputDispatcher類的mCurrentInputTargets成員變量中去。
Step 14. InputDispatcher.addWindowTargetLocked
void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
BitSet32 pointerIds) {
mCurrentInputTargets.push();
InputTarget& target = mCurrentInputTargets.editTop();
target.inputChannel = window->inputChannel;
target.flags = targetFlags;
target.xOffset = - window->frameLeft;
target.yOffset = - window->frameTop;
target.pointerIds = pointerIds;
這個函數簡單,就是把傳進來的參數window添加到mCurrentInputTargets中去就完事了,後面InputDispatcher就會從mCurrentInputTargets中取出恰當的Activity視窗,然後把鍵盤事件分發給它。
回到Step 12中的dispatchKeyLocked函數,它接下來就調用dispatchEventToCurrentInputTargetsLocked來進一步處理了。
Step 15. InputDispatcher.dispatchEventToCurrentInputTargetsLocked
void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
EventEntry* eventEntry, bool resumeWithAppendedMotionSample) {
......
for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
resumeWithAppendedMotionSample);
} else {
......
}
這個函數的實作也比較簡單,前面我們已經把目前需要接受鍵盤事件的Activity視窗添加到mCurrentInputTargets中去了,是以,這裡就分别把它們取出來,然後調用prepareDispatchCycleLocked函數把鍵盤事件分發給它們處理。
前面我們在分析應用程式注冊鍵盤消息接收通道的過程時,在Step 18中(InputDispatcher.registerInputChannel),把Server端的InputChannel封裝成了一個Connection,然後以這個InputChannel中的Receive Pipe Fd作為鍵值把這個Connection對象儲存在mConnectionsByReceiveFd中。這裡,既然我們已經通過mCurrentInputTargets得到了表示目前需要接收鍵盤事件的Activity視窗的InputTarget對象,而且這個InputTarget對象的inputChannel就表示當初在InputDispatcher中注冊的Server端InputChannel,是以,這裡就可以把這個Connection對象取出來,最後調用prepareDispatchCycleLocked函數來進一步處理。
本文轉自 Luoshengyang 51CTO部落格,原文連結:http://blog.51cto.com/shyluo/966635,如需轉載請自行聯系原作者