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

时间:2019-07-08 10:09:40

标签: android events design-patterns event-handling observer-pattern

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,因为决定返回值(即truefalse)的时间可能会更长,并且该函数不应使主线程处于等待状态。因此,我需要找到其他方法来延迟超级调用。

摆脱这个问题的正确方法是什么?有没有解决这种问题的特定设计模式?

请忽略语言。

更新1

我一直在考虑立即存储keyCodeevent并返回true(表示事件已被使用并且不需要其他观察者重新使用),然后在{{ 1}}已完成,我有operation()可用,现在我可以使用相同的statuskeyCode重新触发未决的存储事件。但是,并非所有事件都提供手动触发的功能。对于无法手动触发的事件,我该怎么办。

我建议的解决方案

event

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

2 个答案:

答案 0 :(得分:1)

因为事件是一劳永逸的(没有返回值或无效),所以应该清楚的是,异步事件或返回值的事件处理程序本身就是矛盾或自相矛盾。当异步意味着“等待而不阻塞”而事件“通知而不等待”时,您显然在制造比解决方案更多的问题。返回值还表示调用方正在等待操作完成并对结果感兴趣。

我建议重组您的应用程序。
事件处理程序永远不要返回值或异步。

  

如果观察者需要更多时间来决定是否食用,该怎么办?   你如何处理?

此决定(或一般而言的决定)始终取决于至少一个变量的状态。 有两种情况

  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 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(...)