我正在寻找一种更好的方法来在使用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
似乎会在有非空项目的情况下运行两次,这是一个交易破坏者。
答案 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
所有这些都是按需延迟完成的。
答案 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