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

来源:互联网 发布:改图片格式的软件 编辑:程序博客网 时间:2024/05/24 06:16
参考 深入理解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;        }...}



原创粉丝点击