RxJava:令人困惑的行为

时间:2018-10-30 13:55:29

标签: java android rx-java rx-android

我希望类中的方法在IO线程上运行一些代码,但是只有它们所订阅的Subject具有一定的值。然后,调用方应在Android UI线程上得到响应。

类似这样的东西:

public class MyClass {

  private final Subject<Boolean, Boolean> subject;
  private final OtherClass otherObject;

  public MyClass(Subject<Boolean, Boolean> subject,
      OtherClass otherObject) {
    this.subject = subject;
    this.otherObject = otherObject;
  }

  public Observable<String> myMethod() {
    return waitForTrue(() -> otherObject.readFromDisk());
  }

  private <T> Observable<T> waitForTrue(Callable<T> callable) {
    return subject
        .first(value -> value)
        .flatMap(value -> Observable.fromCallable(callable))
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread());
  }
}

这有效吗?不确定,所以我写了一组单元测试来检查它们。我发现我的测试方法尽管在逐个运行时始终有效,但是作为套件的一部分会失败。

事实上,我发现如果我两次进行相同的测试,它将第一次通过,但是第二次失败!

public class MyClassTest {

  private TestScheduler ioScheduler;
  private TestScheduler androidScheduler;
  private TestSubscriber<String> testSubscriber;
  private MyClass objectUnderTest;

  @Before public void setup() {
    ioScheduler = new TestScheduler();
    androidScheduler = new TestScheduler();
    testSubscriber = new TestSubscriber<>();
    RxJavaHooks.reset();
    RxJavaHooks.setOnIOScheduler(scheduler -> ioScheduler);
    RxAndroidPlugins.getInstance().reset();
    RxAndroidPlugins.getInstance().registerSchedulersHook(
        new RxAndroidSchedulersHook() {
          @Override public Scheduler getMainThreadScheduler() {
            return androidScheduler;
          };
        });
    Subject<Boolean, Boolean> subject = BehaviorSubject.create(true);
    MyClass.OtherClass otherClass = mock(MyClass.OtherClass.class);
    when(otherClass.readFromDisk()).thenReturn("mike");;
    objectUnderTest = new MyClass(subject, otherClass);
  };

  @Test public void firstTest() {
    objectUnderTest.myMethod().subscribe(testSubscriber);
    ioScheduler.advanceTimeBy(1, TimeUnit.SECONDS);
    androidScheduler.advanceTimeBy(1, TimeUnit.SECONDS);
    testSubscriber.assertValueCount(1);
    // This passes
  };

  @Test public void secondTest() {
    firstTest();
    // This fails!
  };
}

为什么会这样?并且是测试中的类中的错误还是测试代码?

我认为使用RxJava 1.x可能是个问题,但是我对RxJava 2.x也有类似的问题。

编辑:由于测试代码中缺少一行,因此测试失败。您必须将其放入设置方法中:

AndroidSchedulers.reset()

因为钩子只被AndroidSchedulers类的静态初始化程序调用一次。

1 个答案:

答案 0 :(得分:1)

subscribeOnSubject没有实际影响,因为它们没有订阅副作用,无法迁移到另一个线程。因此,当他们获得新商品时,会在调用者线程上通知其消费者。将项目移动到另一个线程应该通过observeOn完成:

private <T> Observable<T> waitForTrue(Callable<T> callable) {
    return subject
    .filter(value -> value)
    .take(1)
    .observeOn(Schedulers.io())
    .map(value -> callable.call())
    .observeOn(AndroidSchedulers.mainThread());
}

此外,您不需要flatMap来执行callable,映射就足够了。