大多数的按键驱动,即使按键一直按着,也不会一直上报事件,而是只上报一个down事件,那对于事件的重复上报,自然而然就交给了Android 的输入子系统。
在InputDispatcher线程中,对于按键事件,调用dispatchKeyLocked处理
c
//frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
// Preprocessing.
if (!entry->dispatchInProgress) {//dispatchInProgress默认为false
if (entry->repeatCount == 0 && entry->action == AKEY_EVENT_ACTION_DOWN &&//按下
(entry->policyFlags & POLICY_FLAG_TRUSTED) && //事件来自于InputReader
(!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {//没有设置过禁止重复上报的flag
if (mKeyRepeatState.lastKeyEntry &&
mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
//能进入到这里,说明在驱动上,已经上报了重复按下的事件,Android就不用再处理了
entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
resetKeyRepeatLocked();
mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
} else {
// Not a repeat. Save key down state in case we do see a repeat later.
resetKeyRepeatLocked();
mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout;//keyRepeatTimeout默认是400ms
}
mKeyRepeatState.lastKeyEntry = entry;//将当前事件保存在lastKeyEntry 中
entry->refCount += 1;//引用加1
} else if (!entry->syntheticRepeat) {
resetKeyRepeatLocked();
}
if (entry->repeatCount == 1) {//repeatCount 等于1时,就设置长按事件的flag
entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
} else {
entry->flags &= ~AKEY_EVENT_FLAG_LONG_PRESS;
}
entry->dispatchInProgress = true;
logOutboundKeyDetails("dispatchKey - ", *entry);
}
//省略
这个方法主要是将当前事件保存在了mKeyRepeatState的lastKeyEntry 变量中,并设置该事件经过keyRepeatTimeout会再次上报。keyRepeatTimeout是通过JNI,调用到ViewConfiguration 类中的getLongPressTimeout方法获取的,默认是400ms
c
//frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
//省略
mKeyRepeatState.lastKeyEntry = nullptr;
policy->getDispatcherConfiguration(&mConfig);
}
//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
ATRACE_CALL();
JNIEnv* env = jniEnv();
jint keyRepeatTimeout = env->CallIntMethod(mServiceObj,
gServiceClassInfo.getKeyRepeatTimeout);
if (!checkAndClearExceptionFromCallback(env, "getKeyRepeatTimeout")) {
outConfig->keyRepeatTimeout = milliseconds_to_nanoseconds(keyRepeatTimeout);
}
//省略
}
//frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
// Native callback.
private int getKeyRepeatTimeout() {
return ViewConfiguration.getKeyRepeatTimeout();
}
//frameworks\base\core\java\android\view\ViewConfiguration.java
public static int getKeyRepeatTimeout() {
return getLongPressTimeout();
}
public static int getLongPressTimeout() {
return AppGlobals.getIntCoreSetting(Settings.Secure.LONG_PRESS_TIMEOUT,
DEFAULT_LONG_PRESS_TIMEOUT);//默认是400ms
}
上面的方法只是保存了当前事件,并设置了下次上报的时间间隔是400ms,那再次上报是在哪里上报的呢?
在InputDispatcher线程的循环体内,调用dispatchOnceInnerLocked处理
c
//frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
nsecs_t currentTime = now();
//省略
if (!mPendingEvent) {
if (mInboundQueue.empty()) {
//省略
// Synthesize a key repeat if appropriate.
if (mKeyRepeatState.lastKeyEntry) {//上次已经保存了之前的down事件
if (currentTime >= mKeyRepeatState.nextRepeatTime) {//如果当前时间超过了400ms,表示要再次上报了
mPendingEvent = synthesizeKeyRepeatLocked(currentTime);//取出事件
} else {
if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
*nextWakeupTime = mKeyRepeatState.nextRepeatTime;
}
}
}
//省略
可以看出,如果lastKeyEntry不为空,而且当前时间超过了之前设置的400ms后,就会调用synthesizeKeyRepeatLocked去取出事件,进行后续的分发
c
//frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) {
KeyEntry* entry = mKeyRepeatState.lastKeyEntry;//得到事件
// Reuse the repeated key entry if it is otherwise unreferenced.
uint32_t policyFlags = entry->policyFlags &
(POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED);
if (entry->refCount == 1) {
entry->recycle();
entry->id = mIdGenerator.nextId();
entry->eventTime = currentTime;
entry->policyFlags = policyFlags;
entry->repeatCount += 1;
} else {
KeyEntry* newEntry =
new KeyEntry(mIdGenerator.nextId(), currentTime, entry->deviceId, entry->source,
entry->displayId, policyFlags, entry->action, entry->flags,
entry->keyCode, entry->scanCode, entry->metaState,
entry->repeatCount + 1, entry->downTime);//根据上次的down事件,重新生成一个事件,action,keycode这些都是一样的,注意新的KeyEntry的repeatCount 会加1
mKeyRepeatState.lastKeyEntry = newEntry;//重新设置lastKeyEntry ,以供下一次重复上报
entry->release();
entry = newEntry;
}
entry->syntheticRepeat = true;
entry->refCount += 1;
mKeyRepeatState.nextRepeatTime = currentTime + mConfig.keyRepeatDelay;//后面重复事件隔keyRepeatDelay就会去上报,keyRepeatDelay是通过调用ViewConfiguration的getKeyRepeatDelay得到的,默认是50ms
return entry;
}
可以看出,这里取出的是上次保存的事件,并根据上次事件的各个参数的值,重新生成KeyEntry,去重复上报。后续的上报处理流程就和之前的流程是一样的。需要注意的是,后续重复事件上报的间隔默认为50ms
总结
- 如果驱动已经上报了重复事件,InputDispatcher是不会去重新生成事件上报的
- 第一次重复事件上报的间隔时间默认为400ms,后续的间隔默认为50ms
- 每次上报的时候,repeatCount 都会增加 1
- 当repeatCount 等于 1 的时候,就会设置长按的flag,也就是默认按键按下400ms,就会认为是长按事件。如果我们要修改长按的时间的话,修改Settings.Secure.LONG_PRESS_TIMEOUT默认值即可