Fallback Observable for RxJava

前端 未结 6 1418
-上瘾入骨i
-上瘾入骨i 2021-02-02 00:25

I\'m in search for a better way to achieve a simple Observable fallback system for empty results when using RxJava. The idea is that, if a local query for a set of data results

相关标签:
6条回答
  • 2021-02-02 01:08

    Use switchIfEmpty operator.

    There is example of usage:

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

    Simplified with 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")
    );
    
    0 讨论(0)
  • 2021-02-02 01:10

    After some research and looking at other peoples responses, I believe using a Transformer is the most robust solution, like so...

    Observable.from(Collections.emptyList())
      .compose(new Observable.Transformer<List<Object>, List<Object>>() {
        @Override
        public Observable<List<Object> call(Observable<List<Object>> source) {
          boolean isEmpty = observable.isEmpty().toBlocking().first();
          if (isEmpty) {
            return backupObservable();
          } else {
            return source;
          }
        }
      });
    
    0 讨论(0)
  • 2021-02-02 01:13

    I'm sure there's more elegant solution to this problem but you could simply use flatMap in this case. Since you're already dealing the List<Object> which is returned from queryLocalDatabase() method you could do something like this.

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

    I've replace Object for String for the purpose of this code.

    0 讨论(0)
  • 2021-02-02 01:14

    You can concatenate your Observables as well:

    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
    
    0 讨论(0)
  • 2021-02-02 01:19

    I found a more elegant solution to this problem.

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

    The key contender here is cache which replays the original emitted values.

    Keep in mind the javadoc about cache.

    You sacrifice the ability to unsubscribe from the origin when you use the cache Observer so be careful not to use this Observer on Observables that emit an infinite or very large number of items that will use up memory.

    0 讨论(0)
  • 2021-02-02 01:22

    Another variant. In my case I need to try one cached entry first, then lazily attempt the other if not present:

    public void test() {
        String cacheRowKey = "row";
        Observable<String> = cacheLookup(cacheRowKey, "newVersion").switchIfEmpty(
            Observable.defer(() -> cacheLookup(cacheRowKey, "legacyVersion").switchIfEmpty(
            Observable.defer(() -> onMiss(cacheRowKey;
    }
    
    private Observable<String> cacheLookup(String key, String version) {
        // Delegate to the real cache - will return effectively
        // Observable.just(xxx) or Observable.empty()
        ...
    }
    
    private Observable<String> onMiss(String key) {
        // do the expensive lookup to then populate the cache
        ...
    }
    
    1. This first tries a lookup in cache for the key and 'new' version
    2. On cache miss, tries the cache again for the 'legacy' version
    3. On cache misses for both 'new' and 'legacy' versions, perform the expensive lookup (presumably to populate the cache).

    All of which are lazily done on demand.

    0 讨论(0)
提交回复
热议问题