1

I'm refactoring some code that would call a web service with a formatted date parameter ("2016-3-10" for example), and if that returned null, would fire off another method with a date one day earlier (like "2016-3-9"). This would happen for at least 3 retries.

I'm refactoring this into RxJava and am not sure how to implement the backoff strategy or which .retry() or .retryWhen() operator to use in this situation.

I have the web service returning the usual Observable using Retrofit.

This is what I have currently:

 PictureService pictureService = retrofit.create(PictureService.class);

        pictureService.getPhotos(date)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .retry() //Some retry

                ...
                .subscribe()

So which operator would be best used here for retrying the requests, and how should I pass in a new date to the getPhotos() request when the current date returns null?

Just for the sake of completion here is the Retrofit service that was mentioned above:

@GET("/someurl/photos?api_key=xxxxxxx")
Observable<PictureAPI> getPhotos(@Query("earth_date") String earthDate);
Orbit
  • 2,985
  • 9
  • 49
  • 106
  • I disagree that this is a duplicate, as I'm not implementing any backoff strategy utilizing timing. I would like to know how to implement the logic for retrying when the response returns null. – Orbit Mar 11 '16 at 08:20
  • 1
    I also don't think this is a duplicate. @Orbit: I don't think the `retry` family will get you anywhere here. 1.) They usually kick in when on `onError` comes down the chain. You could transform any `null`s to errors and then `retry` but 2) you actually don't want to retry at all: You want a different Observable if the first one fails... But, now that I am writing this, I have an idea... stay tuned! – david.mihola Mar 11 '16 at 12:51
  • 1
    Oh, damn, I can't answer this anymore, since it's supposedly a duplicate... Well... Look here: http://blog.danlew.net/2015/06/22/loading-data-from-multiple-sources-with-rxjava/ And then think of this `Observable.just("2016-3-10", "2016-3-9", "2016-3-8").flatMap(date -> pictureService.getPhotos(date)).filter(response -> response != null).take(1)...` – david.mihola Mar 11 '16 at 12:55
  • I guess that *could* work, but would make 3 requests initially and then filter afterwards, which isn't too pretty. Will try it out and keep looking, thanks! – Orbit Mar 11 '16 at 13:35
  • 1
    Ah yes, had to run to a meeting and of course I messed it up... You need `concatMap` instead of `flatMap`. This will make sure that each Observable is only subscribed to when the previous proved inadeqate... As Dan explains in his blog post: `concat` (and `concatMap`) will subscribe to each new Observable only after the previous one has emitted `onCompleted` AND only if more items are needed downstream. The `take(1)` will unsubscribe after the first non-`null` response and therefore `concatMap` will just not subscriber to the next Observable. – david.mihola Mar 11 '16 at 13:52

1 Answers1

3

OK, now that I can answer this question again, here we go:

Observable.just("2016-3-10", "2016-3-9", "2016-3-8")
.concatMap(date -> pictureService.getPhotos(date))
.filter(response -> response != null)
.take(1)...

For a detailed explanation take a look at this blog post by Dan Lew: http://blog.danlew.net/2015/06/22/loading-data-from-multiple-sources-with-rxjava/

In short: concat (and concatMap) will subscribe to each new Observable only after the previous one has emitted onCompleted AND only if more items are needed downstream. The take(1) will unsubscribe after the first non-null response and therefore concatMap will just not subscribe to the next Observable.

EDIT: To check that really only one request is sent you could

1.) Enable logging in Retrofit (if you are recent version by adding an Interceptor to the OkHttpClient). This lets you directly observe the requests that are being sent.

2.) Add a doOnSubscribe() like this:

.concatMap(date -> pictureService.getPhotos(date)
    .doOnSubscribe(new Action0() {
        @Override
        public void call() {
            Log.d(TAG, "sending request for " + date);   
        }
    });
)

Since Retrofit requests are only sent upon subscription the log message will appear if and only if a request is then sent.

david.mihola
  • 12,062
  • 8
  • 49
  • 73