欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Input系統(tǒng)分發(fā)策略及其應(yīng)用示例詳解

 更新時(shí)間:2023年02月03日 09:35:36   作者:大胃粥  
這篇文章主要為大家介紹了Input系統(tǒng)分發(fā)策略及其應(yīng)用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

Input系統(tǒng): 按鍵事件分發(fā) 從整體上描繪了通用的事件分發(fā)過程,其中有兩個(gè)比較的環(huán)節(jié),一個(gè)是截?cái)嗖呗?,一個(gè)是分發(fā)策略。Input系統(tǒng):截?cái)嗖呗缘姆治雠c應(yīng)用 分析了截?cái)嗖呗约捌鋺?yīng)用,本文來分析分發(fā)策略及其應(yīng)用。

在正式開始分析前,讀者務(wù)必仔細(xì)地閱讀 Input系統(tǒng): 按鍵事件分發(fā) ,了解截?cái)嗖呗院头职l(fā)策略的執(zhí)行時(shí)機(jī)。否則,閱讀本文沒有意義,反而是浪費(fèi)時(shí)間。

分發(fā)策略原理

根據(jù) Input系統(tǒng): 按鍵事件分發(fā) 可知,分發(fā)策略發(fā)生在事件分發(fā)的過程中,并且發(fā)生在事件分發(fā)循環(huán)前,如下

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
                                        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    // ...
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
        // ...
    }
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
        if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            if (INPUTDISPATCHER_SKIP_EVENT_KEY != 0) {
                // ...
            }
            // 創(chuàng)建一個(gè)命令,當(dāng)命令被執(zhí)行的時(shí)候,
            // 回調(diào) doInterceptKeyBeforeDispatchingLockedInterruptible()
            std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
                    &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
            sp<IBinder> focusedWindowToken =
                    mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry));
            commandEntry->connectionToken = focusedWindowToken;
            commandEntry->keyEntry = entry;
            // 把剛創(chuàng)建的命令,加入到隊(duì)列 mCommandQueue 中
            postCommandLocked(std::move(commandEntry));
            // 返回 false 等待命令執(zhí)行
            return false; // wait for the command to run
        } else {
            // ...
        }
    } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
        // ...
    }
    // ...
    // 啟動(dòng)分發(fā)循環(huán),把事件分發(fā)給目標(biāo)窗口
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

如代碼所示,事件在分發(fā)給窗口前,會(huì)先執(zhí)行分發(fā)策略。而執(zhí)行分發(fā)策略的方式是創(chuàng)建一個(gè)命令 CommandEntry,然后保存到命令隊(duì)列中。

當(dāng)命令被執(zhí)行的時(shí)候,會(huì)執(zhí)行 InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible() 函數(shù)。

那么,為何要執(zhí)行分發(fā)策略呢?有如下兩點(diǎn)原因

  • 截?cái)嗍录o系統(tǒng)一個(gè)優(yōu)先處理事件的機(jī)會(huì)。
  • 實(shí)現(xiàn)組合按鍵功能。

例如,導(dǎo)航欄上的 home, app switch 按鍵的功能就是在這里實(shí)現(xiàn)的,分發(fā)策略會(huì)截?cái)嗨鼈儭?/p>

Input系統(tǒng):截?cái)嗖呗缘姆治雠c應(yīng)用 可知,截?cái)嗖呗砸部梢越財(cái)嗍录屜到y(tǒng)優(yōu)先處理事件。那么截?cái)嗖呗耘c分發(fā)策略有什么區(qū)別呢?

Input系統(tǒng): 按鍵事件分發(fā) 可知,截?cái)嗖呗允翘幚硪恍┫到y(tǒng)級(jí)的事件,例如 power 鍵亮滅屏,這些事件的處理必須讓用戶感覺沒有延時(shí)。假如 power 鍵的事件是在分發(fā)流程中處理的,那么必須等到 power 事件前面的所有事件都處理完畢,才能輪到 power 事件被處理,這就可能讓用戶感覺系統(tǒng)有點(diǎn)不流暢。

而分發(fā)策略處理一些優(yōu)先級(jí)相對(duì)較低的系統(tǒng)事件,例如 home,app switch 事件。由于分發(fā)策略處于分發(fā)過程中,因此當(dāng)一個(gè) app 在發(fā)生 anr 期間,無論我們按多少次 home, app switch 按鍵,系統(tǒng)都會(huì)沒有響應(yīng)。

好,回歸正題,如上面代碼所示,為了執(zhí)行分發(fā)策略,創(chuàng)建了一個(gè)命令,并保存到命令隊(duì)列,然后就返回了。由 Input系統(tǒng): 按鍵事件分發(fā) 可知,返回到了 InputDispatcher 的線程循環(huán),如下

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        std::scoped_lock _l(mLock);
        mDispatcherIsAlive.notify_all();
        // 1. 如果沒有命令,分發(fā)一次事件
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }
        // 2. 執(zhí)行命令
        // 這個(gè)命令來自于前一步的事件分發(fā)
        if (runCommandsLockedInterruptible()) {
            // 馬上開始下一次的線程循環(huán)
            nextWakeupTime = LONG_LONG_MIN;
        }
        // 處理 ANR ,并返回下一次線程喚醒的時(shí)間。
        const nsecs_t nextAnrCheck = processAnrsLocked();
        nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
        if (nextWakeupTime == LONG_LONG_MAX) {
            mDispatcherEnteredIdle.notify_all();
        }
    } // release lock
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    // 3. 線程休眠 timeoutMillis 毫秒
    mLooper->pollOnce(timeoutMillis);
}

第1步,執(zhí)行事件分發(fā),不過事件為了執(zhí)行分發(fā)策略,創(chuàng)建了一個(gè)命令并保存到命令隊(duì)列中。

第2步,執(zhí)行命令隊(duì)列中的命令。根據(jù)前面創(chuàng)建命令時(shí)所分析的,會(huì)調(diào)用如下函數(shù)

void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
        CommandEntry* commandEntry) {
    // 取出命令中保存的按鍵事件
    KeyEntry& entry = *(commandEntry->keyEntry);
    KeyEvent event = createKeyEvent(entry);
    mLock.unlock();
    android::base::Timer t;
    const sp<IBinder>& token = commandEntry->connectionToken;
    // 執(zhí)行分發(fā)策略
    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry.policyFlags);
    if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
        ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
              std::to_string(t.duration().count()).c_str());
    }
    mLock.lock();
    // 分發(fā)策略的結(jié)果保存到 KeyEntry::interceptKeyResult
    if (delay < 0) {
        entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
    } else if (!delay) {
        entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
    } else {
        entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
        entry.interceptKeyWakeupTime = now() + delay;
    }
}

果然,命令在執(zhí)行時(shí)候,為事件 KeyEntry 查詢了分發(fā)策略,并把分發(fā)策略的結(jié)果保存到 KeyEntry::interceptKeyResult。

注意,分發(fā)策略最終是由上層執(zhí)行的,如果要截?cái)嗍录?,那么需要返回?fù)值,如果不截?cái)?,返?,如果暫時(shí)不知道如何處理事件,那么返回正值。

第2步執(zhí)行完畢后,會(huì)立刻開始下一次的線程循環(huán)。如果要理解這一點(diǎn),需要理解底層的消息機(jī)制,讀者可能參考我寫的 深入理解Native層的消息機(jī)制。

在下一次線程循環(huán)時(shí),執(zhí)行第1步時(shí),在事件分發(fā)給窗口前,需要根據(jù)分發(fā)策略的結(jié)果,對(duì)事件做進(jìn)一步的處理,如下

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
                                        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    // ...
    // 1. 分發(fā)策略的結(jié)果表示稍后再嘗試分發(fā)事件
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
        // 還沒到超時(shí)的時(shí)間,計(jì)算線程休眠的時(shí)間,讓線程休眠
        if (currentTime < entry->interceptKeyWakeupTime) {
            if (entry->interceptKeyWakeupTime < *nextWakeupTime) {
                *nextWakeupTime = entry->interceptKeyWakeupTime;
            }
            return false; // wait until next wakeup
        }
        // 重置分發(fā)策略的結(jié)果,為了再一次查詢分發(fā)策略
        // 當(dāng)再次查詢分發(fā)策略時(shí),分發(fā)策略會(huì)給出是否截?cái)嗟慕Y(jié)果
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
        entry->interceptKeyWakeupTime = 0;
    }
    // Give the policy a chance to intercept the key.
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
        // 執(zhí)行分發(fā)策略
        // ...
    } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
        // 2. 分發(fā)策略的結(jié)果表示路過這個(gè)事件,也就是丟棄這個(gè)事件
        // 這里設(shè)置了丟棄的原因,下面會(huì)根據(jù)這個(gè)原因,丟棄事件,不會(huì)分發(fā)給窗口
        if (*dropReason == DropReason::NOT_DROPPED) {
            *dropReason = DropReason::POLICY;
        }
    }
    // 事件有原因需要丟棄,不執(zhí)行后面的分發(fā)循環(huán)
    if (*dropReason != DropReason::NOT_DROPPED) {
        setInjectionResult(*entry,
                           *dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
                                                             : InputEventInjectionResult::FAILED);
        mReporter->reportDroppedKey(entry->id);
        return true;
    }
    // ...
    // 啟動(dòng)分發(fā)循環(huán),把事件分發(fā)給目標(biāo)窗口
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

對(duì)各種分發(fā)結(jié)果的處理如下

  • INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER : 上層暫時(shí)不知道如何處理這個(gè)事件,所以告訴底層等一會(huì)再看看。底層收到這個(gè)結(jié)果,會(huì)讓線程休眠指定時(shí)間。當(dāng)時(shí)間到了后,會(huì)把重置分發(fā)策略結(jié)果為 INTERCEPT_KEY_RESULT_UNKNOWN,然后再次查詢分發(fā)策略,此時(shí)分發(fā)策略會(huì)給出一個(gè)明確的結(jié)果,到底是截?cái)噙€是不截?cái)唷?/li>
  • INTERCEPT_KEY_RESULT_SKIP :上層截?cái)嗔诉@個(gè)事件,因此讓底層跳過這個(gè)事件,也就是不丟棄這個(gè)事件。
  • INTERCEPT_KEY_RESULT_CONTINUE : 源碼中沒有明確處理這個(gè)結(jié)果,很簡單嘛,那就是繼續(xù)后面的事件分發(fā)流程。

那么,什么時(shí)候上層不知道如何處理一個(gè)事件呢?這是為了實(shí)現(xiàn)組合鍵的功能。

當(dāng)?shù)谝粋€(gè)按鍵按下時(shí),分發(fā)策略不知道用戶到底會(huì)不會(huì)按下第二個(gè)按鍵,因此它會(huì)告訴底層再等等吧,底層因此休眠了。

如果在底層休眠期間,如果用戶按下了第二個(gè)按鍵,那么成功觸發(fā)組合鍵的功能,當(dāng)?shù)讓有褋頃r(shí),再次為第一個(gè)按鍵的事件查詢分發(fā)策略,此時(shí)分發(fā)策略知道第一個(gè)按鍵的事件已經(jīng)觸發(fā)了組合鍵功能,因此告訴底層,第一個(gè)按鍵事件截?cái)嗔?,也就是被上層處理了,那么底層就不?huì)分發(fā)這第一個(gè)按鍵的事件。

如果在底層休眠期間,如果沒有用戶按下了第二個(gè)按鍵。當(dāng)?shù)讓有褋頃r(shí),再次為第一個(gè)按鍵的事件查詢分發(fā)策略,此時(shí)分發(fā)策略知道第一個(gè)按鍵事件沒有觸發(fā)組合鍵的功能,因此告訴底層這個(gè)事件不截?cái)?,繼續(xù)分發(fā)處理吧。

下面以一個(gè)具體的組合鍵以例,來理解分發(fā)策略,因此讀者務(wù)必仔細(xì)理解上面所分析的。

分發(fā)策略的應(yīng)用 - 組合鍵

以手機(jī)上最常見的截?cái)嘟M合鍵為例,也就是 電源鍵 + 音量下鍵,來理解分發(fā)策略。但是,請(qǐng)讀者務(wù)必,先仔細(xì)理解上面所分析的。

組合鍵的功能是由 KeyCombinationManager 管理,它在 PhoneWindowManager 的初始化如下

// PhoneWindowManager.java
    private void initKeyCombinationRules() {
        // KeyCombinationManager 是用來實(shí)現(xiàn)組合按鍵功能的類
        mKeyCombinationManager = new KeyCombinationManager();
        // 配置默認(rèn)為 true
        final boolean screenshotChordEnabled = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_enableScreenshotChord);
        if (screenshotChordEnabled) {
            // 添加 電源鍵 + 音量下鍵 組合按鍵規(guī)則
            mKeyCombinationManager.addRule(
                    new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) {
                        @Override
                        void execute() {
                            mPowerKeyHandled = true;
                            // 截屏
                            interceptScreenshotChord();
                        }
                        @Override
                        void cancel() {
                            cancelPendingScreenshotChordAction();
                        }
                    });
        }
        // ... 省略其它組合鍵的規(guī)則
    }

很簡單,創(chuàng)建一個(gè)規(guī)則用于實(shí)現(xiàn)截屏,并保存到了 KeyCombinationManager#mRules 中。

當(dāng)按下電源鍵,首先會(huì)經(jīng)過截?cái)嗖呗蕴幚?,注意不是分發(fā)策略

// PhoneWindowManager.java
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        // ...
        if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
            // 1. 處理按鍵手勢
            // 包括組合鍵
            handleKeyGesture(event, interactiveAndOn);
        }        
        switch (keyCode) {
            // ...
            case KeyEvent.KEYCODE_POWER: {
                // 2. power 按鍵事件是不傳遞給用戶的
                result &= ~ACTION_PASS_TO_USER;
                // ..
                break;
            }
            // ...
        }
        // ...
        return result;
    }

第2步,截?cái)嗖呗詴?huì)截?cái)嚯娫窗存I事件。

第1步,截?cái)嗖呗蕴幚戆存I手勢,這其中就包括組合鍵

// PhoneWindowManager.java
private void handleKeyGesture(KeyEvent event, boolean interactive) {
    if (mKeyCombinationManager.interceptKey(event, interactive)) {
        // handled by combo keys manager.
        mSingleKeyGestureDetector.reset();
        return;
    }
    // ...
}

現(xiàn)在來看下 KeyCombinationManager 如何處理截屏功能的第一個(gè)按鍵事件,也就是電源事件

boolean interceptKey(KeyEvent event, boolean interactive) {
    final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
    final int keyCode = event.getKeyCode();
    final int count = mActiveRules.size();
    final long eventTime = event.getEventTime();
    // 交互狀態(tài),一般指亮屏的狀態(tài)
    // 從這里可以看出,組合鍵的功能,必須在交互狀態(tài)下執(zhí)行
    if (interactive && down) {
        if (mDownTimes.size() > 0) {
            // ...
        }
        if (mDownTimes.get(keyCode) == 0) {
            // 1. 記錄按鍵按下的時(shí)間
            mDownTimes.put(keyCode, eventTime);
        } else {
            // ignore old key, maybe a repeat key.
            return false;
        }
        if (mDownTimes.size() == 1) {
            mTriggeredRule = null;
            // 2. 獲取所有與按鍵相關(guān)的規(guī)則,保存到 mActiveRules
            forAllRules(mRules, (rule)-> {
                if (rule.shouldInterceptKey(keyCode)) {
                    mActiveRules.add(rule);
                }
            });
        } else {
            // ...
        }
    } else {
        // ...
    }
    return false;
}

KeyCombinationManager 處理組合鍵的第一個(gè)按鍵事件很簡單,保存了按鍵按下的時(shí)間,并找到與這個(gè)按鍵相關(guān)的規(guī)則并保存。

由于電源按鍵事件被截?cái)啵?dāng)執(zhí)行到分發(fā)策略時(shí),如下

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
                                        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    // ...
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
        // ...
    }
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
        if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            // ...不被截?cái)嗟氖录艜?huì)創(chuàng)建命令,用于執(zhí)行分發(fā)策略...
            return false; // wait for the command to run
        } else {
            // 1. 被截?cái)嗟氖录?,繼續(xù)后面的分發(fā)流程,最終會(huì)被丟棄
            entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
        }
    } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
        // ...
    }
    // 2. 如果事件被截?cái)嗔耍蜁?huì)在這里被丟棄
    if (*dropReason != DropReason::NOT_DROPPED) {
        setInjectionResult(*entry,
                           *dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
                                                             : InputEventInjectionResult::FAILED);
        mReporter->reportDroppedKey(entry->id);
        return true;
    }
    // ...
    // 啟動(dòng)分發(fā)循環(huán),把事件分發(fā)給窗口
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

被截?cái)嗖呗越財(cái)嗟氖录粫?huì)經(jīng)過分發(fā)策略的處理,并且直接被丟棄。這就是窗口為何收不到 power 按鍵事件的根本原因。

截?cái)嗟牡谝粋€(gè)事件,電源事件,已經(jīng)分析完畢?,F(xiàn)在假設(shè)用戶在很短的時(shí)間內(nèi),按鍵下了音量下鍵。經(jīng)過截?cái)嗖呗詴r(shí),仍然首先經(jīng)過手勢處理,此時(shí) KeyCombinationManager 處理第二個(gè)按鍵的過程如下

boolean interceptKey(KeyEvent event, boolean interactive) {
    final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
    final int keyCode = event.getKeyCode();
    final int count = mActiveRules.size();
    final long eventTime = event.getEventTime();
    if (interactive && down) {
        if (mDownTimes.size() > 0) {
            if (count > 0
                    && eventTime > mDownTimes.valueAt(0) + COMBINE_KEY_DELAY_MILLIS) {
                // 第二個(gè)按鍵按下超時(shí)
                forAllRules(mActiveRules, (rule)-> rule.cancel());
                mActiveRules.clear();
                return false;
            } else if (count == 0) { // has some key down but no active rule exist.
                return false;
            }
        }
        if (mDownTimes.get(keyCode) == 0) {
            // 保存第二個(gè)按鍵按下的時(shí)間
            mDownTimes.put(keyCode, eventTime);
        } else {
            // ignore old key, maybe a repeat key.
            return false;
        }
        if (mDownTimes.size() == 1) {
            // ...
        } else {
            // Ignore if rule already triggered.
            if (mTriggeredRule != null) {
                return true;
            }
            // check if second key can trigger rule, or remove the non-match rule.
            forAllActiveRules((rule) -> {
                // 需要在規(guī)則的時(shí)間內(nèi)按下第二個(gè)按鍵,才能觸發(fā)規(guī)則
                if (!rule.shouldInterceptKeys(mDownTimes)) {
                    return false;
                }
                Log.v(TAG, "Performing combination rule : " + rule);
                // 觸發(fā)組合鍵規(guī)則
                rule.execute();
                // 保存已經(jīng)觸發(fā)的規(guī)則
                mTriggeredRule = rule;
                return true;
            });
            // 清空 mActiveRules,保存已經(jīng)觸發(fā)的規(guī)則
            mActiveRules.clear();
            if (mTriggeredRule != null) {
                mActiveRules.add(mTriggeredRule);
                return true;
            }
        }
    } else {
        // ...
    }
    return false;
}

根據(jù)代碼可知,只有組合鍵的第二個(gè)按鍵在規(guī)定的時(shí)間內(nèi)按下(150ms),才能觸發(fā)規(guī)則。對(duì)于 電源鍵 + 音量下鍵,就是觸發(fā)截屏。

截?cái)嗖呗栽谔幚戆存I手勢時(shí),現(xiàn)在已經(jīng)觸發(fā)截屏,那么它是否截?cái)嘁袅肯骆I呢?如果音量下鍵不用來掛斷電話,那就不截?cái)?,這段代碼請(qǐng)讀者自行分析。

我們假設(shè)音量下鍵沒有被截?cái)嗖呗越財(cái)啵敲串?dāng)它經(jīng)過分發(fā)策略時(shí),如何處理呢?如下

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
                                        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    // ...
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
        // ...
    }
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
        // 1. 對(duì)于不被截?cái)嗟氖录?,?chuàng)建命令執(zhí)行分發(fā)策略
        if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
                    &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
            sp<IBinder> focusedWindowToken =
                    mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry));
            commandEntry->connectionToken = focusedWindowToken;
            commandEntry->keyEntry = entry;
            postCommandLocked(std::move(commandEntry));
            return false; // wait for the command to run
        } else {
            // ...
        }
    } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
        // ...
    }
    // ...
    // 啟動(dòng)分發(fā)循環(huán),分發(fā)事件
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

音量下鍵事件要執(zhí)行分發(fā)策略,分發(fā)策略最終由上層的 PhoneWindowManager 實(shí)現(xiàn),如下

// PhoneWindowManager.java
public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
        int policyFlags) {
    // ...
    final long key_consumed = -1;
    if (mKeyCombinationManager.isKeyConsumed(event)) {
        // 返回 -1,表示截?cái)嗍录?
        return key_consumed;
    }
}
// KeyCombinationManager.java
boolean isKeyConsumed(KeyEvent event) {
    if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) {
        return false;
    }
    // 在觸發(fā)組合鍵功能時(shí),mTriggeredRule 保存了觸發(fā)的規(guī)則
    return mTriggeredRule != null && mTriggeredRule.shouldInterceptKey(event.getKeyCode());
}

由于已經(jīng)觸發(fā)了截屏功能,因此分發(fā)策略對(duì)音量下鍵的處理結(jié)果是 -1,也就是截?cái)嗨?/p>

底層收到這個(gè)截?cái)嘈畔r(shí),就會(huì)丟棄音量下鍵這個(gè)事件,如下

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
                                        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    // ...
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
        // ...
    }
    // Give the policy a chance to intercept the key.
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
        // ...
    } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
        // 1. 分發(fā)策略的結(jié)果是事件被截?cái)?
        if (*dropReason == DropReason::NOT_DROPPED) {
            *dropReason = DropReason::POLICY;
        }
    }
    // 2. 丟棄被截?cái)嗟氖录?
    if (*dropReason != DropReason::NOT_DROPPED) {
        setInjectionResult(*entry,
                           *dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
                                                             : InputEventInjectionResult::FAILED);
        mReporter->reportDroppedKey(entry->id);
        return true;
    }
    // ...
    // 啟動(dòng)分發(fā)循環(huán),發(fā)送事件給窗口
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

由于音量下鍵事件被丟棄,因此窗口也收不到這個(gè)事件。其實(shí),組合鍵功能只要觸發(fā),兩個(gè)按鍵事件,窗口都收不到。

截屏功能不是只能通過 電源鍵 + 音量下鍵 觸發(fā),還可以通過 音量下鍵 + 電源鍵觸發(fā),但是分析過程卻和上面不一樣。如果音量下鍵先按,那么分發(fā)策略會(huì)返回一個(gè)稍后再試的結(jié)果,如果讀者有興趣,可以自行分析。

結(jié)束

通過學(xué)習(xí)本文,我們要達(dá)到學(xué)以致用的目的,其實(shí)最主要的,就是要學(xué)會(huì)如何自定義組合鍵。對(duì)于硬件上新增的按鍵事件,如果要截?cái)?,可以在截?cái)嗖呗裕部梢栽诜职l(fā)策略,根據(jù)自己所認(rèn)為的重要性級(jí)別來決定。

以上就是Input系統(tǒng)分發(fā)策略及其應(yīng)用示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Input系統(tǒng)分發(fā)策略的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論