Как отложить события для следующих наблюдателей? - PullRequest
0 голосов
/ 08 июля 2019
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) может быть больше, и функция не должна заставлять основной поток ждать. Поэтому мне нужно найти другой способ сделать супер-вызов отложенным.

Как правильно избавиться от этой проблемы? Какой-либо конкретный шаблон дизайна для решения этой проблемы?

Пожалуйста, не обращайте внимания на язык.

Обновление 1

Я думал о том, чтобы сохранить keyCode и event и вернуть true (означает, что событие было израсходовано, и нет необходимости повторно потреблять его другим наблюдателем) немедленно, а затем, после завершения operation(), у меня есть status доступно, и теперь я могу повторно вызвать ожидающее сохраненное событие с теми же keyCode и event. Но не все события предоставляют функции для запуска вручную. Как я могу сделать это для событий, которые не могут быть вызваны вручную.

Мое предлагаемое решение

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

Это правильный путь? Что может быть плохого в этой мысли?

Ответы [ 2 ]

1 голос
/ 10 июля 2019

Поскольку события запускаются и забываются (нет возвращаемых значений или недействительны), должно быть ясно, что асинхронные события или обработчики событий, которые возвращают значение, сами по себе являются противоречием или парадоксом.Когда асинхронный означает «ожидание без блокировки» и событие «уведомить без ожидания», вы, очевидно, создаете больше проблем, чем решений.Возвращаемое значение также подразумевает, что вызывающая сторона ожидает завершения операции и заинтересована в результате.

Я рекомендую реструктурировать ваше приложение.
Обработчик событий никогда не должен возвращать значение или быть асинхронным.

что если наблюдателю нужно больше времени, чтобы решить, потребляет ли он или нет?Как вы справляетесь с этим?

Это решение (или решения в целом) всегда зависит от состояния хотя бы одной переменной.Существуют две ситуации:

  1. , когда состояние известно в тот момент, когда наблюдатель получает уведомление, или
  2. состояние в данный момент неизвестно (наблюдателю требуется больше времени).

Ситуация 1) не требует ожидания, но ситуация 2) делает.
В случае ситуация 2) , изменениесостояние всегда вызывается операцией.Продолжительность выполнения этой операции определяет продолжительность времени ожидания.Эта операция должна вызвать событие, когда соответствующее состояние изменилось.

В общем, у вас есть три варианта ожидания:

  1. Продолжайте вращаться, пока не будет выполнено условие, подобное опросу (например, бесконечноеloop): while (true) {}.
  2. Используйте таймер и после истечения времени выполните действие
  3. Используйте события всякий раз, когда вам нужно ждать.

Первые два варианта заблокируют поток.Если поток совпадает с наблюдаемым потоком, то вы блокируете наблюдаемый и всех других ожидающих наблюдателей тоже.Если этот поток является потоком пользовательского интерфейса, то пользовательский интерфейс остановится и перестанет отвечать на запросы.События - это шаблон, который решит проблему блокировки.


Давайте представим следующий сценарий: вы хотите запустить определенную анимацию.У вас есть два ограничения: тип анимации зависит от того, какая клавиша была нажата И, прежде чем вы сможете запустить новую анимацию, вы должны дождаться завершения первой.Например, когда нажата 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).Чтобы сохранить значение изменения (аргументы события) события, вводится частное общее поле, чтобы второй обработчик событий мог получить к нему доступ и, наконец, обработать его.

0 голосов
/ 10 июля 2019

Использование 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 Ознакомьтесь с официальной документацией о том, как использовать

Редактировать 1 фрагмент кода

RxView.clicks(mButton) .debounce(300, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) .subscribe(...)

...