当前位置: 首页 > 知识库问答 >
问题:

RxJava:令人困惑的行为

殳飞扬
2023-03-14

我希望类中的方法在IO线程上运行一些代码,但只有一次它们订阅的主题具有特定值。然后调用者应该在AndroidUI线程上得到响应。

像这样的东西:

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

为什么会这样?被测类中的bug是,还是测试代码?

我认为使用RxJava 1可能会有问题。x、 但我对RxJava 2也有类似的问题。x。

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

AndroidSchedulers.reset()

因为钩子只被AndroidSchedulers类的静态初始值设定项调用过一次。

共有1个答案

邴英毅
2023-03-14

订阅对主题没有实际影响,因为它们没有订阅的副作用,不会转移到另一个线程。因此,当他们获得一个新项目时,他们会在调用者线程上通知他们的消费者。应通过观察将项目移动到另一个线程:

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

此外,您不需要仅为了执行可调用的map,map就足够了。

 类似资料:
  • 问题内容: 有人可以向我解释一下吗: 我从不理解,我认真地认为,如果有人不尝试向我深入解释,我永远也不会。这整个上下文使我感到困惑。有时它是这样的: 有时它不会那样工作,但只接受: 有时是: 等等等等等。 我了解上下文的基础,但是有几项?为什么日食会一次又一次地向我抛出错误呢?为什么有时需要声明上下文?: 我无法在所有情况下都找到合适的环境,我怎么知道在每种情况下都合适的环境? 问题答案: 首先,

  • 这是一个练习2D数组的练习,显然我在理解它们方面失败了。输入是创建一个方法,该方法在数组[][]中查找最大的沙漏形整数数。数组的大小总是6x6,因此循环是x<4和y<4,整数值也是从-9到9,这就是为什么我的结果变量以-256开始(如果我以0开始,充满负值的数组将不起作用) 样本输入 即产生输出的沙漏形状 然后在main中使用我的biggestHourglass()方法。 我的结果不符合预期,我不

  • 问题内容: 我总是将If语句(在C#中)用作(1.替代); 我知道没有必要将“ == true”写为(2.替代)); 但是,我使用它是因为它更具可读性,并且不会引起性能问题。当然,这是我的选择,而且我知道许多软件开发人员都喜欢第一种选择。最佳用法是什么,为什么? 问题答案: 如果布尔值的名称清楚地说明了它的含义,那么我将始终选择版本2。但是,有时您会陷入一个特别晦涩的变量名,至少不能更改它。现在就

  • 错误:错误:CreateProcess:没有这样的文件或目录 Gradle控制台中的详细日志是: 失败:生成失败,出现异常。 很高兴提到在这个错误之前,我有一个“在结构中重新定义”的错误。当我集中精力时,我发现在两个link.h文件中有两个类的重复定义,一个在项目中,另一个在我的sdk\ndk-bundle目录中。所以我在我的sdk中评论了link.h中的冲突,我就面临了上面的错误。但奇怪的是,当

  • 我试图理解java.util.random.NextInt(int n)是如何工作的,尽管进行了所有的搜索甚至调试,但我还是不能完全理解它的实现。 造成混乱的是while循环:http://docs.oracle.com/javase/7/docs/api/java/util/random.html#NextInt(int) 我意识到这应该是为了解决模数偏见,但很难看到如何解决。

  • 问题内容: 公共PrintWriter(OutputStream out,boolean autoFlush) : 公共PrintStream(OutputStream out,boolean autoFlush) : 问题答案: 我认为答案就在于Java的历史。三人,并在日期回Java 1.0。在此之前,该语言已经内置了对文件编码和字符集的严重支持。 引用Javadoc: “ PrintStre