3

I have an EditText where user inputs a search query and I want to perform an instant search on my server when user types something.

I try to do this with RxJava as follows:

RxTextView.textChanges(editQuery) // I'm using RxBinding for listening to text changes
    .flatMap(new Func1<CharSequence, Observable<UserPublic[]>>() {
        @Override
        public Observable<UserPublic[]> call(CharSequence query) {
            return api.searchUsers(query); // I'm using Retrofit 1.9 for network calls. searchUsers returns an Observable<UserPublic[]>
        }
    })
    .subscribe(Observers.create(
        new Action1<UserPublic[]>() {
            @Override
            public void call(UserPublic[] userPublics) {
                processResult(userPublics);
            }
        })
        , new Action1<Throwable>() {
            @Override
            public void call(Throwable throwable) {
                processError(throwable);
            }
    });

the problem is that if the network call encounters an error, the whole observable stops. So when user continues typing, nothing happens.

How can I modify this code so that:

  1. Whenever there is a network problem, processError is called
  2. But when user continues typing, new network calls continue to be issued (leading to processResult / processError again)
netimen
  • 4,199
  • 6
  • 41
  • 65

2 Answers2

3

Use the retryWhen() operator and attach it to the observable chain before the subscription. Note that the argument to retryWhen() is a function that takes an Observable<Throwable> and returns a Observable<?>. It doesn't matter the type returned as the operator uses the onNext() result to initiate a retry and the onError() or onCompleted() results to terminate the chain.

Here is a naive application that waits 5 seconds and tries again:

observable
  .retryWhen( errorObservable -> errorObservable.delay( 5, TimeUnit.SECONDS ) )
  .subscribe();

Here is a less naive operation that retries on time out, and fails if an IOException occurs:

observable
  .retryWhen( errorObservable -> errorObservable.flatMap( throwable -> { // (1)
       if ( throwable instanceof IOException ) {
         return Observable.error( throwable ); // (2)
       }
       return Observable.just(1); // (3)
     } )
  .subscribe();
  1. Using flatMap() allows you to defer the decision until you know what kind of error you are dealing with.
  2. The observable that is returned throws the provided error or something else that you want to better describe your issue.
  3. Providing an observable that simply does onNext() tells the retryWhen() operator to resubscribe to the original observable.
Bob Dalgleish
  • 8,167
  • 4
  • 32
  • 42
0

I can suggest you take a look at the Error Handling Operators page in the RxJava documentation. Experiment with different operators to come up with the one that suits your use case best. I think you should probably go with some kind of default value returned by onErrorResumeNext(), e.g. returning a new UserPublic[0]. Calling processError here would be problematic, especially if it deals with UI, since you're probably still processing on a background thread.

Egor
  • 39,695
  • 10
  • 113
  • 130