Android11重复事件上报流程

大多数的按键驱动,即使按键一直按着,也不会一直上报事件,而是只上报一个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默认值即可
相关推荐
fatiaozhang952731 分钟前
中兴云电脑W102D_晶晨S905X2_2+16G_mt7661无线_安卓9.0_线刷固件包
android·adb·电视盒子·魔百盒刷机·魔百盒固件
CYRUS_STUDIO2 小时前
Android APP 热修复原理
android·app·hotfix
鸿蒙布道师2 小时前
鸿蒙NEXT开发通知工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
鸿蒙布道师2 小时前
鸿蒙NEXT开发网络相关工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
大耳猫2 小时前
【解决】Android Gradle Sync 报错 Could not read workspace metadata
android·gradle·android studio
ta叫我小白2 小时前
实现 Android 图片信息获取和 EXIF 坐标解析
android·exif·经纬度
dpxiaolong4 小时前
RK3588平台用v4l工具调试USB摄像头实践(亮度,饱和度,对比度,色相等)
android·windows
tangweiguo030519875 小时前
Android 混合开发实战:统一 View 与 Compose 的浅色/深色主题方案
android
老狼孩111225 小时前
2025新版懒人精灵零基础及各板块核心系统视频教程-全分辨率免ROOT自动化开发
android·机器人·自动化·lua·脚本开发·懒人精灵·免root开发
打死不学Java代码6 小时前
PaginationInnerInterceptor使用(Mybatis-plus分页)
android·java·mybatis