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默认值即可
相关推荐
拭心9 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
带电的小王11 小时前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
梦想平凡11 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
元争栈道12 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
阿甘知识库13 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
元争栈道13 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频
MuYe13 小时前
Android Hook - 动态加载so库
android
居居飒14 小时前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
Henry_He17 小时前
桌面列表小部件不能点击的问题分析
android
工程师老罗17 小时前
Android笔试面试题AI答之Android基础(1)
android