参考 深入理解Android卷三 - 深入理解Android输入系统
重复按键的产生:在用户持续按下一个按键到抬起之间,应用程序能够收到多个onKeyDown时间,并且getReaptCount的返回值会不断累加,并且当且仅当第二次调用KeyEvent.isLongPress返回true。z这个工作是InputDispatcher来完成。
虽然有些按键输入设备支持按键重复按下事件的回报工作(如KeyboardInputMapper中做过了重复按键的容错处理),但是大部分输入设备仅上报初次按下和抬起两个事件,因此,InputDispatcher必须对重复按键事件作出模拟。这个模拟过程在InputDispatcher.dispatchOnceInnerLocked中实现。
第一步 重复按键开启 bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { // Preprocessing. if (! entry->dispatchInProgress) { if (entry->repeatCount == 0 && entry->action == AKEY_EVENT_ACTION_DOWN && (entry->policyFlags & POLICY_FLAG_TRUSTED) && (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) { if (mKeyRepeatState.lastKeyEntry && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) { // We have seen two identical key downs in a row which indicates that the device // driver is automatically generating key repeats itself. We take note of the // repeat here, but we disable our own next key repeat timer since it is clear that // we will not need to synthesize key repeats ourselves. //硬件主动上报了重复按键事件,则关闭模拟 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; } mKeyRepeatState.lastKeyEntry = entry; entry->refCount += 1; } else if (! entry->syntheticRepeat) { //不满足上述四个条件,说明不需要模拟重复按键,但是有可能是重复按键派发,因此需要根据syntheticRepeat对重复按键事件放行 resetKeyRepeatLocked(); } //处理长按事件 if (entry->repeatCount == 1) { entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS; } else { entry->flags &= ~AKEY_EVENT_FLAG_LONG_PRESS; } entry->dispatchInProgress = true; logOutboundKeyDetailsLocked("dispatchKey - ", entry); } ...... } 开启重复按键模拟操作的条件有4个。为了理解这四个条件,需要注意一点,模拟的重复按键除了其来源不是派发队列以外,其他方面都与真实的按键一样,也会进入dispatchKeyLocked进行派发。 四个条件如下: repeatedCount = 0; eventCount = ACTION_DOWN 拥有Trust选项,即事件来源于InputReader 事件没有POLICY_FLAG_DISABLE_KEY_REPEAT选项,这个选项有键盘设备HandlesKeyRepeat决定(dumpsys input可以查找HandlesKeyRepeat得到输入设备该项配置),在InputReader.processKey中设置,一般是false。 所以一般长按某个键都能满足这四个条件。 而关闭重复模拟按键的条件 硬件上报了一个重复按下的事件。说明硬件支持按下事件的重复上报,因此也就不需要进行模拟。 不满足上述四个条件,并且不是模拟的重复按键。 进行重复按键由mConfig的两个字段keyRepeatTimeOut(500ms)和keyRepeatDelay(50ms)来规定派发重复按键的时间点。 第二步重复按键派发 void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { nsecs_t currentTime = now(); ....... // Ready to start a new event. // If we don't already have a pending event, go grab one. //mPendingEvent当前正在派发的输入事件,派发完成置为null if (! mPendingEvent) { if (mInboundQueue.isEmpty()) { ...... // Synthesize a key repeat if appropriate. if (mKeyRepeatState.lastKeyEntry) { //当前时间等于或晚于nextRepeatTime,说明重复按键事件已到,需要产生一个重复按键 if (currentTime >= mKeyRepeatState.nextRepeatTime) { mPendingEvent = synthesizeKeyRepeatLocked(currentTime); } else { // 模拟重复按键时间没到,那么修改派发睡眠时间,以便唤醒派发线程模拟重复按键 if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) { *nextWakeupTime = mKeyRepeatState.nextRepeatTime; } } } // Nothing to do if there is no pending event. // 没有事件需要派发,直接退出,以便进入Looper睡眠 if (!mPendingEvent) { return; } } else { // Inbound queue has at least one entry. mPendingEvent = mInboundQueue.dequeueAtHead(); traceInboundQueueLengthLocked(); } // Poke user activity for this event. if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) { pokeUserActivityLocked(mPendingEvent); } 第三步 重复按键的生成 InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) { KeyEntry* entry = mKeyRepeatState.lastKeyEntry; // Reuse the repeated key entry if it is otherwise unreferenced. // 继承了被模拟事件的策略,但是排除了Disptcherpolicy赋予的派发策略。 uint32_t policyFlags = entry->policyFlags & (POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED); // 如果entry可以被重用,则重用 if (entry->refCount == 1) { entry->recycle(); entry->eventTime = currentTime; entry->policyFlags = policyFlags; entry->repeatCount += 1; } else { KeyEntry* newEntry = new KeyEntry(currentTime, entry->deviceId, entry->source, policyFlags, entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount + 1, entry->downTime); mKeyRepeatState.lastKeyEntry = newEntry; entry->release(); entry = newEntry; } entry->syntheticRepeat = true; // Increment reference count since we keep a reference to the event in // mKeyRepeatState.lastKeyEntry in addition to the one we return. entry->refCount += 1; //设置下次模拟重复按键的时间keyRepeatDelay 50ms mKeyRepeatState.nextRepeatTime = currentTime + mConfig.keyRepeatDelay; return entry; }
小米6 Activity界面长按back键产生的log,
第一重复按键500ms延时,每个重复按键50ms间隔
12-01 14:45:39.244 13457-13457/com.royole.simpledatashare D/zhanghao: onKeyDown: 4 event:KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_BACK, scanCode=158, metaState=0, flags=0x48, repeatCount=0, eventTime=158804661, downTime=158804661, deviceId=7, source=0x101 } 12-01 14:45:39.744 13457-13457/com.royole.simpledatashare D/zhanghao: onKeyDown: 4 event:KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_BACK, scanCode=158, metaState=0, flags=0xc8, repeatCount=1, eventTime=158805162, downTime=158804661, deviceId=7, source=0x101 } 12-01 14:45:39.744 13457-13457/com.royole.simpledatashare D/zhanghao: onKeyLongPress: 4 event:KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_BACK, scanCode=158, metaState=0, flags=0x400000c8, repeatCount=1, eventTime=158805162, downTime=158804661, deviceId=7, source=0x101 } 12-01 14:45:39.795 13457-13457/com.royole.simpledatashare D/zhanghao: onKeyDown: 4 event:KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_BACK, scanCode=158, metaState=0, flags=0x48, repeatCount=2, eventTime=158805213, downTime=158804661, deviceId=7, source=0x101 } 12-01 14:45:39.845 13457-13457/com.royole.simpledatashare D/zhanghao: onKeyDown: 4 event:KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_BACK, scanCode=158, metaState=0, flags=0x48, repeatCount=3, eventTime=158805263, downTime=158804661, deviceId=7, source=0x101 } ... 12-01 14:45:43.304 13457-13457/com.royole.simpledatashare D/zhanghao: onKeyDown: 4 event:KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_BACK, scanCode=158, metaState=0, flags=0x48, repeatCount=71, eventTime=158808717, downTime=158804661, deviceId=7, source=0x101 } 12-01 14:45:43.313 13457-13457/com.royole.simpledatashare D/zhanghao: onKeyUp: 4 event:KeyEvent { action=ACTION_UP, keyCode=KEYCODE_BACK, scanCode=158, metaState=0, flags=0x248, repeatCount=0, eventTime=158808729, downTime=158804661, deviceId=7, source=0x101 }
应用监测长按事件需要在onKeyDown里面,加入以下代码,这样才能监听到长按事件
if
(keyCode == KeyEvent.
KEYCODE_DPAD_RIGHT
){
event.startTracking(); //开始跟踪
return true
;
}
具体原因可以查看源码KeyEvent.dispatch()方法。
case ACTION_DOWN: { mFlags &= ~FLAG_START_TRACKING; if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state + ": " + this); boolean res = receiver.onKeyDown(mKeyCode, this); if (state != null) { if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) { if (DEBUG) Log.v(TAG, " Start tracking!"); state.startTracking(this, target); } else if (isLongPress() && state.isTracking(this)) { try { if (receiver.onKeyLongPress(mKeyCode, this)) { if (DEBUG) Log.v(TAG, " Clear from long press!"); state.performedLongPress(this); res = true; } } catch (AbstractMethodError e) { } } } return res;
而长按处理是在重复按键逻辑中,在用户持续按下一个按键到抬起之间,应用程序往往能够收到多次onKyeDown的调用,每次onKeyDown调用时的KeyEven.geReaptedCount的返回值会不断累加,并且当且仅当第二次调用时KeyEvent.isLongPress()的返回值为true。具体代码可以查看重复按键乘胜逻辑。
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { ... if (entry->repeatCount == 1) { entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS; } else { entry->flags &= ~AKEY_EVENT_FLAG_LONG_PRESS; } ... }