override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (event.action == KeyEvent.ACTION_DOWN) {
val status = operation() // operation takes time
return status
}
return super.onKeyDown(keyCode, event)
}
当事件发生时,上述处理程序将被调用。现在,如果需要时间来决定是否将true
或false
状态传递到if块内的下一层(超级),则如何正确设计流程。我需要获得结果asynchronously
,因为决定返回值(即true
或false
)的时间可能会更长,并且该函数不应使主线程处于等待状态。因此,我需要找到其他方法来延迟超级调用。
摆脱这个问题的正确方法是什么?有没有解决这种问题的特定设计模式?
请忽略语言。
我一直在考虑立即存储keyCode
和event
并返回true
(表示事件已被使用并且不需要其他观察者重新使用),然后在{{ 1}}已完成,我有operation()
可用,现在我可以使用相同的status
和keyCode
重新触发未决的存储事件。但是,并非所有事件都提供手动触发的功能。对于无法手动触发的事件,我该怎么办。
event
这是正确的方法吗?这个想法有什么坏处?
答案 0 :(得分:1)
因为事件是一劳永逸的(没有返回值或无效),所以应该清楚的是,异步事件或返回值的事件处理程序本身就是矛盾或自相矛盾。当异步意味着“等待而不阻塞”而事件“通知而不等待”时,您显然在制造比解决方案更多的问题。返回值还表示调用方正在等待操作完成并对结果感兴趣。
我建议重组您的应用程序。
事件处理程序永远不要返回值或异步。
如果观察者需要更多时间来决定是否食用,该怎么办? 你如何处理?
此决定(或一般而言的决定)始终取决于至少一个变量的状态。 有两种情况
情况1)不需要等待,但是情况2)则需要等待。
在<情况> 2)的情况下,状态更改始终由操作触发。该操作的执行持续时间决定了等待时间。当相关状态更改时,此操作必须引发一个事件。
通常,您有三个选择要等待:
前两个选项将阻塞线程。如果线程与可观察线程相同,则您也将阻塞可观察者和所有其他等待的观察者。如果该线程是UI线程,则UI将停止并变得无响应。事件是一种可以解决阻塞问题的模式。
让我们想象一下以下情况:您想开始一个特定的动画。您有两个限制:动画的类型取决于按下的键,并且在开始新动画之前,您必须等到第一个完成。例如。按下 TAB 时,从左向右移动一个矩形。当按下 ENTER 时,将一个矩形从顶部移到底部。
这引入了两种等待状态:按键按下和动画完成。要处理等待,您将为每个潜在的等待情况创建并关联一个事件:keyPressed
和animationStopped
事件:
键盘按键按下事件
观察者要实现的接口,它正在等待按下特定的键:
interface IKeyPressedListener {
void onKeyPressed(int keyCode);
}
要公开并引发事件的可观察对象要实现的事件接口:
interface IKeyPressedEvent {
void subscribeToKeyPressedEvent(IKeyPressedListener listener);
void unsubscribeToKeyPressedEvent(IKeyPressedListener listener);
}
动画事件
正在等待动画停止的观察者要实现的接口:
interface IAnimationStoppedListener {
void onAnimationStopped();
}
要公开并引发事件的可观察对象要实现的事件接口:
interface IAnimationStoppedEvent {
void subscribeToAnimationStoppedEvent(IAnimationStoppedListener listener);
void unsubscribeToAnimationStoppedEvent(IAnimationStoppedListener listener);
}
实际事件监听器
在按下按键时播放动画的类的实现:
class AnimationController implements IKeyPressedListener, IAnimationStoppedListener
{
// store the key that was pressed,
// so that an event that will be raised at a later can process it
private int keyCodeOfLastKeyPressed = 0;
// The reference to the class that exposes
// the keyPressedEvent by implementing IKeyPressedEvent
KeyboardController keyboardController;
// The reference to the class that exposes
// the animationStoppedEvent by implementing IAnimationStoppedEvent
AnimationPlayer animationPlayer;
// Constructor
public AnimationController() {
this.keyboardController = new KeyboardController();
this.animationPlayer = new AnimationPlayer();
// Subscribe to the key pressed event
this.keyboardController.subscribeToKeyPressedEvent(this);
}
@Override
public void onKeyPressed(int keyCode) {
if (this.animationPlayer.hasPlayingAnimation) {
// Instead of waiting that the animation completes
// subscribe to an event and store the relevant data
this.keyCodeOfLastKeyPressed = keyCode;
this.animationPlayer.subscribeToAnimationStoppedEvent(this);
}
else {
// There is no playing animation, so no need to wait
this.animationPlayer.playAnimation(keyCode);
}
}
// After a while this handler will be invoked by the event source.
@Override
public void onAnimationStopped() {
// To avoid memory leaks unsubscribe first
this.animationPlayer.unsubscribeToAnimationStoppedEvent(this);
// Since we stored the key code earlier, we can continue to process it
// and start a new animation that maps to a specific key
this.animationPlayer.playAnimation(this.keyCodeOfLastKeyPressed);
}
}
遵循观察者模式可以避免线程阻塞等待时间。应用程序可以仅离开上下文并在事件发生时返回(在这种情况下为AnimationStopped
事件)。为了存储事件的更改值(事件args),引入了一个私有共享字段,以便第二个事件处理程序可以访问并最终对其进行处理。
答案 1 :(得分:0)
使用观察者模式可能会对您有所帮助。
You can use Debounce operator (debounce(DEBOUNCE_TIMEOUT.toLong(), TimeUnit.MILLISECONDS)) to delay the event.
only emit an item from an Observable if a particular timespan has passed without it emitting another item
Check the official documentation for how to use
编辑1 代码段
RxView.clicks(mButton)
.debounce(300, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.subscribe(...)