包装侦听器的自定义Observable忽略了SubscribeOn运算符

时间:2016-08-14 23:05:13

标签: rx-java

我有以下设置:

负责更新值的类,该类还提供通过回调侦听此值更改的方法:

private static class ValueUpdater<T> {
    Listener<T> listener;

    void update(T value) {
      if (listener != null) this.listener.onValueUpdated(value);
    }

    void setListener(Listener<T> listener) {
      this.listener = listener;
    }

    interface Listener<T> {
      void onValueUpdated(T value);
    }
  }

将该类使用的侦听器包装到Observable

的方法
private static <T> Observable<T> createValueObservable(ValueUpdater<T> valueUpdater) {
    return Observable.create(subscriber -> {
      valueUpdater.setListener(value -> {
        System.out.println("Listener: " + Thread.currentThread().getName());
        if (!subscriber.isUnsubscribed()) {
          try {
            subscriber.onNext(value);
          } catch (Exception e) {
            subscriber.onError(e);
          }
        }
      });

      subscriber.add(Subscriptions.create(() -> valueUpdater.setListener(null)));
    });
  }

然后像这样调用:

public static void main(String[] args) throws Exception {
    final ValueUpdater<Integer> valueUpdater = new ValueUpdater<>();

    createValueObservable(valueUpdater)
        .subscribeOn(Schedulers.io())
        .subscribe(i -> System.out.println("Next: " + Thread.currentThread().getName()));

    Thread.sleep(1000);

    valueUpdater.update(1);
    valueUpdater.update(2);
    valueUpdater.update(3);
  }

这给了我以下输出:

Listener: main
Next: main
Listener: main
Next: main
Listener: main
Next: main

我知道发生这种情况是因为在主线程上调用ValueUpdater.update()并导致Observable在内部切换线程并最终忽略SubscribeOn()运算符,如{{3 }}

这种情况的最佳替代方案是什么,考虑到我无法修改ValueUpdater类,我希望能够将Scheduler应用于上游Observable?< / p>

编辑:为了让它更清晰一点,我的问题的关键是不明白为什么会这样。我想要了解的是围绕此行为设计代码的最佳方法,防止侦听器回调在主线程上运行。

我想要实现的输出是这样的:

Listener: RxIoScheduler-3
Next: RxIoScheduler-3
Listener: RxIoScheduler-3
Next: RxIoScheduler-3
Listener: RxIoScheduler-3
Next: RxIoScheduler-3

.observeOn()不会这样做,因为它只会影响下游的项目。如果我要应用它,输出将如下所示:

Listener: main
Listener: main
Listener: main
Next: RxIoScheduler-3
Next: RxIoScheduler-3
Next: RxIoScheduler-3

我尝试使用Observable.fromAsync(),如下所示,但它也不起作用:

private static <T> Observable<T> createValueObservable(ValueUpdater<T> valueUpdater) {
    return Observable.fromAsync(emitter -> {
      final CompositeSubscription cs = new CompositeSubscription();
      final Scheduler.Worker worker = Schedulers.io().createWorker();
      final ValueUpdater.Listener<T> listener = new ValueUpdater.Listener<T>() {
        @Override public void onValueUpdated(T value) {
          System.out.println("Listener: " + Thread.currentThread().getName());
          try {
            emitter.onNext(value);
          } catch (Exception e) {
            emitter.onError(e);
          }
        }
      };

      valueUpdater.setListener(listener);

      cs.add(worker);
      cs.add(Subscriptions.create(() -> valueUpdater.setListener(null)));

      emitter.setSubscription(cs);
    }, AsyncEmitter.BackpressureMode.BUFFER);
  }

我现在能想到的唯一可行解决方案是始终从另一个线程调用ValueUpdater.update(),但我很确定这对于一个真实的用例是不实际的:

public static void main(String[] args) throws Exception {
    final ValueUpdater<Integer> valueUpdater = new ValueUpdater<>();

    createValueObservable1(valueUpdater)
        .subscribeOn(Schedulers.io())
        .subscribe(i -> System.out.println("Next: " + Thread.currentThread().getName()));

    Completable.fromAction(() -> {
      valueUpdater.update(1);
      valueUpdater.update(2);
      valueUpdater.update(3);
    }).subscribeOn(Schedulers.io()).subscribe();

    Thread.sleep(1000);
  }

1 个答案:

答案 0 :(得分:1)

.subscribeOn()仅修改订阅.subscribe(...)发生的位置(我不认为在您的情况下它是必要的,因为您没有io /计算您的订阅)。

您还应该使用.observeOn()来修改项目处理的位置。

编辑:无论你如何骰子,valueUpdater.update()的主体都将与调用者在同一个线程中运行。但是,当您立即切换到Rx时,这可能是您正在寻找的内容:

private static <T> Observable<T> createValueObservable(ValueUpdater<T> valueUpdater) {
  return Observable.create(subscriber -> {
    valueUpdater.setListener(value -> {
      if (!subscriber.isUnsubscribed()) {
        try {
          subscriber.onNext(value);
        } catch (Exception e) {
          subscriber.onError(e);
        }
      }
    });

    subscriber.add(Subscriptions.create(() -> valueUpdater.setListener(null)));
  })
  .observeOn(Schedulers.io())
  .doOnNext(any -> System.out.println("Listener: " + Thread.currentThread().getName()));
}