参考 深入理解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;
}
...
}