如何为下一个观察者延迟事件?

How to delay events for next observers?

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)
}

当事件发生时,将调用上述处理程序。现在,如果需要时间来决定是否将 truefalse 状态传递给 if 块内的下一层(超级),如何正确设计流程。我需要得到结果 asynchronously 因为决定 return 值(即 truefalse)的时间可能更长,而且函数不应该让主线程等待.所以,我需要找到其他方法来延迟 super 调用。

摆脱这个问题的正确方法是什么?有没有具体的设计模式来处理这类问题?

请忽略语言。

更新 1

我一直在想立即存储keyCodeevent和returntrue(意味着事件被消费了,不需要其他观察者重新消费) ,然后在 operation() 完成后,我有 status 可用,现在我可以使用相同的 keyCodeevent 重新触发待处理的存储事件。但并非所有事件都提供手动触发的功能。对于无法手动触发的事件,我该如何处理。

我提出的解决方案

private fun doOperation(callback: (status:Boolean) -> Unit) {
    Handler().postDelayed({
        callback.invoke( arrayOf(true, false).random() )
    }, 5000)
}

var pendingEvent: KeyEvent? = null

override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
    if (event.action == KeyEvent.ACTION_DOWN) {
        doOperation {
            if (it && pendingEvent != null){
                dispatchKeyEvent(pendingEvent)
                pendingEvent = null
            } else {
                // do nothing
            }
        }
        return true // let other know I consumed it
    }
    return super.onKeyDown(keyCode, event)
}

这是正确的方法吗?这个想法有什么坏处?

因为事件是fire-and-forget(没有return值或无效)所以应该清楚异步事件或return值的事件处理程序本身就是矛盾或悖论.当异步意味着 "waiting without blocking" 和事件 "notify without waiting" 时,您显然会产生比解决方案更多的问题。 return 值还意味着调用者正在等待操作完成并对结果感兴趣。

我建议重组你的申请。
事件处理程序永远不应该 return 一个值或者是异步的。

what if the observer needs more time to decide if it consumes or not? How do you deal it?

这个决定(或一般的决定)总是取决于至少一个变量的状态。 有两种情况

  1. 观察者收到通知时状态已知或
  2. 此时状态未知(观察者需要更多时间)。

情况1)无需等待,但情况2)需要等待。
情况2)的情况下,状态的改变总是由操作触发。该操作的执行时长决定了等待时间的长短。当相关状态发生变化时,此操作必须引发事件。

一般来说,您有三种等待选择:

  1. 一直旋转直到满足轮询等条件(例如无限 循环): while(true){}.
  2. 使用计时器并在经过的时间后执行操作
  3. 在需要等待时使用事件。

前两个选项会阻塞一个线程。如果该线程与可观察线程相同,那么您将阻塞可观察线程和所有其他等待的观察者。如果该线程是 UI 线程,则 UI 将停止并变得无响应。事件是一种解决阻塞问题的模式。


让我们想象一下下面的场景:你想开始一个特定的动画。您有两个约束:动画的类型取决于按下的键,并且在开始新动画之前必须等到第一个动画完成。例如。当按下 TAB 时,从左向右移动一个矩形。当按下 ENTER 时,从上到下移动一个矩形。

这引入了两种等待情况:按键按下和动画完成。要处理等待,您将为每个潜在的等待情况创建并关联一个事件:keyPressedanimationStopped 事件:

键盘按键按下事件

等待特定键被按下的观察者要实现的接口:

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 的实现:

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);
  }
}

遵循观察者模式可以避免线程阻塞等待时间。应用程序可以在事件发生时离开上下文和 return(在本例中为 AnimationStopped 事件)。为了存储事件的变化值(event args),引入了一个私有共享字段,以便第二个事件处理程序可以访问并最终处理它。

使用 Observer-pattern 可能会对您有所帮助。 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(...)