android重复按键与onKeyLongPress长按事件触发

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



  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值