RxJava的后备可观察

时间:2014-08-08 15:16:11

标签: rx-java

我正在寻找一种更好的方法来在使用RxJava时为空结果实现一个简单的Observable回退系统。我们的想法是,如果对一组数据的本地查询导致零项,则应该进行后备查询(可以是网络调用,或其他)。目前,我的代码包含以下内容:

Observable.create(new Observable.OnSubscribe<Object>() {
  @Override
  public void call(Subscriber<? super Object> subscriber) {
     List<Object> results = queryLocalDatabase();
     if (results.isEmpty()) {
       subscriber.onError(new Throwable("Empty results"));
     } else {
       // Process results...
     }
  }
}).onErrorResumeNext(/* Fallback Observable goes here */);

虽然这样可行,但为空结果集抛出异常并没有多大意义。我注意到有isEmpty等条件运算符可用,但它看起来并不像我想要的那样。例如,使用isEmpty ...

localObservable = Observable.create(new Observable.OnSubscribe<Object>() {
  @Override
  public void call(Subscriber<? super Object> subscriber) {
     List<Object> results = queryLocalDatabase();
     for (Object obj : results) {
       // Process object
       subscriber.onNext(object);           
     }
     subscriber.onCompleted();
  }
});

localObservable.isEmpty()
  .switchMap(new Func1<Boolean, Observable<? extends Object>>() {
    @Override
    public Observable<? extends Object> call(Boolean isEmpty) {
      if (isEmpty) {
        // Return fallback Observable.
        return fallbackObservable;
      }

      // Return original Observable, which would mean running the local query again... Not desired.
      return localObservable;
    }
  });

这几乎让我到达了我想去的地方,除了localObservable似乎会在有非空项目的情况下运行两次,这是一个交易破坏者。

6 个答案:

答案 0 :(得分:13)

使用switchIfEmpty运算符。

有使用示例:

Observable.create(new Observable.OnSubscribe<String>() {
    @Override
    public void call(Subscriber<? super String> subscriber) {
        // return no item
        //subscriber.onNext(...);
        System.out.println("Generating nothing :)");
        subscriber.onCompleted();
    }
}).switchIfEmpty(Observable.create(new Observable.OnSubscribe<String>() {
    @Override
    public void call(Subscriber<? super String> subscriber) {
        System.out.println("Generating item");
        subscriber.onNext("item");
        subscriber.onCompleted();
    }
})).subscribe(new Observer<String>() {
    @Override
    public void onCompleted() {
        System.out.println("onCompleted");
    }

    @Override
    public void onError(Throwable e) {
        System.out.println("onError");
    }

    @Override
    public void onNext(String s) {
        System.out.println("onNext: " + s);
    }
});

简化为lamdas:

Observable.create(subscriber -> {
    // return no item
    //subscriber.onNext(...);
    System.out.println("Generating nothing :)");
    subscriber.onCompleted();
}).switchIfEmpty(Observable.create(subscriber -> {
    System.out.println("Generating item");
    subscriber.onNext("item");
    subscriber.onCompleted();
})).subscribe(
    s -> System.out.println("onNext: " + s),
    throwable -> System.out.println("onError"),
    () -> System.out.println("onCompleted")
);

答案 1 :(得分:3)

我确信这个问题有更优雅的解决方案,但在这种情况下你可以简单地使用flatMap。由于您已经在处理从List<Object>方法返回的queryLocalDatabase(),因此您可以执行此类操作。

Observable.create(new Observable.OnSubscribe<List<String>>() {
    @Override
    public void call(Subscriber<? super List<String>> subscriber) {
        List<String> results = queryLocalDatabase();
        subscriber.onNext(results);
        subscriber.onCompleted();
    }

    private List<String> queryLocalDatabase() {
        return Arrays.asList();
    }

}).flatMap(new Func1<List<String>, Observable<String>>() {
    @Override
    public Observable<String> call(List<String> list) {
        if (list.isEmpty()) {
            return getFallbackObservable();
        } else {
            return Observable.from(list);
        }
    }

    private Observable<String> getFallbackObservable() {
        return Observable.from("3", "4");
    }

}).subscribe(new Observer<String>() {
    @Override
    public void onCompleted() {
        System.out.println("onCompleted");
    }

    @Override
    public void onError(Throwable e) {
        System.out.println("onError");
    }

    @Override
    public void onNext(String s) {
        System.out.println("onNext: " + s);
    }
});

为了这段代码的目的,我将String替换为Object。

答案 2 :(得分:0)

我找到了一个更优雅的解决方案来解决这个问题。

Observable<String> cachedObservable =
        Observable.from(Collections.<String>emptyList()) // Swap this line with the below one to test the different cases
        //Observable.from(Arrays.asList("1", "2"))
                .cache();

cachedObservable.isEmpty().switchMap(isEmpty -> {
    if (isEmpty)
        return Observable.from("3", "4");
    else
        return cachedObservable;
}).subscribe(System.out::println);

这里的关键竞争者是cache,它会重放原始发射值。

请记住关于cache的javadoc。

  

当您使用缓存Observer时,您牺牲了取消订阅源的能力,因此请注意不要在Observables上使用此Observer,它会发出无限或大量耗尽内存的项目。

答案 3 :(得分:0)

经过一些研究并观察其他人的反应,我相信使用变压器是最强大的解决方案,就像这样......

IE5.5+

答案 4 :(得分:0)

另一种变体。在我的情况下,我需要首先尝试一个缓存的条目,然后懒惰尝试另一个,如果不存在:

included
  1. 首先尝试在缓存中查找密钥和“新”版本
  2. 在缓存未命中时,再次尝试缓存“遗留”版本
  3. 对于'new'和'legacy'版本的缓存未命中,执行昂贵的查找(可能是为了填充缓存)。
  4. 所有这些都是按需延迟完成的。

答案 5 :(得分:0)

您也可以连接Observable

Observable<Integer> o1 = Observable.empty();
Observable<Integer> o2 = Observable.empty();
Observable<Integer> o3 = Observable.just(3);
Observable<Integer> o4 = Observable.just(4);
Observable<Integer> os = Observable.concat(o1, o2, o3, o4);
Integer v = os.toBlocking().first();  // returns 3