背景:
在平时使用安卓设备过程中经常也会需要用到Key事件,比如手机音量上下键,或者Tv遥控器等,只要涉及到了按键相关事件,就不得不提出一个key事件“长按”的场景,这里的“长按”相对于“短按”来说的。那么这里长短,其实主要是对于手按下key后到抬起的时间间隔,时间间隔短那就代表短按,时间长就代表长按。那么这个间隔到底多少就代表长按呢?而且这个长按到底有哪些作用呢?
针对key的长按,也有学员在vip群中提出一些相关的疑问:


那么本文就带大家来深入剖析长按事件相关原理和源码剖析。
本文原文及作者地址:https://mp.weixin.qq.com/s/mZKJ-yHNGsv79pAMt18uQQ
长按事件深入剖析
输入事件传递大家学过马哥input专题都知道一般有如下3层:

驱动节点部分剖析调研
先从底层开始看,通过getevent命令看看长按事件,是不是驱动层面上报就有多个事件呢?
命令:
adb shell getevent -lrt
输出结果如下:
[ 1427.303112] /dev/input/event13: EV_KEY KEY_VOLUMEDOWN DOWN
[ 1427.303112] /dev/input/event13: EV_SYN SYN_REPORT 00000000
[ 1428.467174] /dev/input/event13: EV_KEY KEY_VOLUMEDOWN UP
[ 1428.467174] /dev/input/event13: EV_SYN SYN_REPORT 00000000 rate 0
明显可以看到对于驱动上报的事件就只有一个DOWN和一个UP,并没有长按相关事件。
那么接下来看看可以看看下一层InputDispatcher部分。
InputDispatcher部分剖析调研
这里对于InputDispatcher部分调研,默认情况下都是没有打开相关日志的,大家可以自行使用命令或者代码进行打开。

再看看这里的logOutboundKeyDetails方法

可以看出需要DEBUG_OUTBOUND_EVENT_DETAILS标志放开才有打印:
如何放开这个标志呢?
frameworks/native/services/inputflinger/dispatcher/DebugConfig.h
/**
* Log detailed debug messages about each outbound event processed by the dispatcher.
* Enable this via "adb shell setprop log.tag.InputDispatcherOutboundEvent DEBUG" (requires restart)
*/
const bool DEBUG_OUTBOUND_EVENT_DETAILS =true;
//__android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "OutboundEvent", ANDROID_LOG_INFO);
上面可以通过prop设置,我这边为了方便直接代码改成true了。
那么看看放开后长按操作后InputDispatcher日志打印:
InputDispatcher: dispatchKey - eventTime=1929429987000, deviceId=0, source=0x301, displayId=-1, policyFlags=0x62000000, action=0x0, flags=0x8, keyCode=0x19, scanCode=0x72, metaState=0x0, repeatCount=0, downTime=1929429987000
InputDispatcher: dispatchKey - eventTime=1929830978346, deviceId=0, source=0x301, displayId=-1, policyFlags=0x42000000, action=0x0, flags=0x88, keyCode=0x19, scanCode=0x72, metaState=0x0, repeatCount=1, downTime=1929429987000
InputDispatcher: dispatchKey - eventTime=1929881359145, deviceId=0, source=0x301, displayId=-1, policyFlags=0x42000000, action=0x0, flags=0x8, keyCode=0x19, scanCode=0x72, metaState=0x0, repeatCount=2, downTime=1929429987000
InputDispatcher: dispatchKey - eventTime=1929932102671, deviceId=0, source=0x301, displayId=-1, policyFlags=0x42000000, action=0x0, flags=0x8, keyCode=0x19, scanCode=0x72, metaState=0x0, repeatCount=3, downTime=1929429987000

可以看到确实在InputDispatcher层面就有长按的log频繁打印,完全不是和驱动上报的event一样只有一个down一个up,这里有若干个repeatCount增长的down类型的key事件派发,第一次repeatCount =1,间隔400ms,而且每个repeat事件间隔事件大概为50ms上下。
而且大家注意对于repeatCount =1时候对于的flag是0x88并不是普通0x8.
那么这个flag差异的是啥?
其实就是如下这个AKEY_EVENT_FLAG_LONG_PRESS标志位:
frameworks/native/include/android/input.h
/**
* This flag is set for the first key repeat that occurs after the
* long press timeout.
*/
AKEY_EVENT_FLAG_LONG_PRESS = 0x80,
也就是只有第一个触发的重复事件才会带上AKEY_EVENT_FLAG_LONG_PRESS这个flag,后面的就不会了。那么这flag有啥作用呢?其实就是方便app层面触发回到Activity的onKeyLongPress方法。
具体可以看看如下源码触发onKeyLongPress:
android/view/KeyEvent.java
这里的主要判断isLongPress方法实现如下:
/**
* For {@link #ACTION_DOWN} events, indicates that the event has been
* canceled as per {@link #FLAG_LONG_PRESS}.
*/
public final boolean isLongPress() {
return (mFlags&FLAG_LONG_PRESS) != 0;
}
/**
* This flag is set for the first key repeat that occurs after the
* long press timeout.
*/
public static final int FLAG_LONG_PRESS = 0x80;
可以看到这里就是依赖这个FLAG_LONG_PRESS为0x80和InputDispatcher层面一样。
InputDispatcher源码剖析
假设正常第一次传递派发key事件:
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<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))) {
//第一次repeatCount为0进入会对nextRepeatTime进行设置,方便后检测时间
// 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->repeatCount == 1设置AKEY_EVENT_FLAG_LONG_PRESS
if (entry->repeatCount == 1) {
entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
} else {
entry->flags &= ~AKEY_EVENT_FLAG_LONG_PRESS;
}
entry->dispatchInProgress = true;
}
//省略其他
//对事件进行派发
// Dispatch the key.
dispatchEventLocked(currentTime, entry, inputTargets);
那么上面第一次调用dispatchKeyLocked核心就是设置了mKeyRepeatState.lastKeyEntry值而且mKeyRepeatState.nextRepeatTime进行赋值。就是当前时间加上mConfig.keyRepeatTimeout

可以看出第一次赋值是timeout是500ms,第二个repeatDelay是50ms,和前面日志看到延时时间一样。
那么接下来派发了第一个后,后面重复事件如何派发呢?就是靠上面设置的timeout。
因为派发完成后,会出现进入dispatchOnce方法
这里又会进入dispatchOnceInnerLocked方法:
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.
if (!mPendingEvent) {
if (mInboundQueue.empty()) {
if (isAppSwitchDue) {
// The inbound queue is empty so the app switch key we were waiting
// for will never arrive. Stop waiting for it.
resetPendingAppSwitchLocked(false);
isAppSwitchDue = false;
}
//判断lastKeyEntry是否有东西
// Synthesize a key repeat if appropriate.
if (mKeyRepeatState.lastKeyEntry) {
//判断当前时间是否到了nextRepeatTime
if (currentTime >= mKeyRepeatState.nextRepeatTime) {
//下一个repeat事件派送时间到了,则合成对应repeat事件
mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
} else {
if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
*nextWakeupTime = mKeyRepeatState.nextRepeatTime;
}
}
}
这里看看synthesizeKeyRepeatLocked是如何合成的:
std::shared_ptr<KeyEntry> InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) {
std::shared_ptr<KeyEntry> entry = mKeyRepeatState.lastKeyEntry;
//初始化一些policyFlags
uint32_t policyFlags = entry->policyFlags &
(POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED);
//用原来的entry构造新的KeyEntry newEntry,newEntry就是新的repeat事件要进行传递
std::shared_ptr<KeyEntry> newEntry =
std::make_unique<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);
newEntry->syntheticRepeat = true;
//新repeat赋值lastKeyEntry
mKeyRepeatState.lastKeyEntry = newEntry;
//重新赋值mKeyRepeatState的nextRepeatTime,就是50ms
mKeyRepeatState.nextRepeatTime = currentTime + mConfig.keyRepeatDelay;
return newEntry;
}
到此长按重复事件就剖析清楚了,简单说就是InputDispatcher会对KeyEvent进行定时500ms启动重复事件派发,要是长按超过500ms,那么就肯定会触发onKeyLongPress方法。
注意这里的500ms是默认值,正常系统会对这个值进行改变,比如上面日志看到是400ms,来源就是设置中settings的long_press_timeout的值:
test@test:~/disk2/aosp14/frameworks$ adb shell settings get secure long_press_timeout
400
更多framework实战干货,请关注“千里马学框架”
958

被折叠的 条评论
为什么被折叠?



