25

Is it possible to use RxJava/RxAndroid and Retrofit to perform periodic http requests to update data every x seconds?

Currently I am using an IntentService and Recursive Handler/Runnable that fires every x seconds. I'd like to know if I could remove all that and let RxJava handle the requests instead.

final RestClient client = new RestClient();
final ApiService service = client.getApiService();

public interface ApiService {
    @GET("/athletes")
    public Observable<List<Athlete>> getAthletes();
}

service.getAthletes()
.retry(3)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<Athlete>>() {
    @Override
    public void call(List<Athlete> athletes) {
        // Handle Success
    }
}, new Action1<Throwable>() {
    @Override
    public void call(Throwable throwable) {
        // Handle Error
    }
});

EDIT

After all is done I ended up with the following code. Any updates are welcome.

final Scheduler scheduler = Schedulers.from(Executors.newSingleThreadExecutor());

obs = Observable.interval(30, TimeUnit.SECONDS, scheduler)
            .flatMap(tick -> service.getAthletes(1, 0l))
            // Performed on service.getAthletes() observable
            .subscribeOn(scheduler)
            .observeOn(AndroidSchedulers.mainThread())
            .doOnError(err -> Log.d("APP", "Error retrieving athletes: " + err.toString()))
            .retry()
            .flatMap(Observable::from)
            .filter(athlete -> {
                // Process Athlete here in the filter
                // Return true to always send messages but could filter them out too
                return true;
            });

public static void startUpdates() {
    if (sub != null) {
        sub = obs.subscribe(athlete -> {
            Log.d("APP", "Done updating athletes! ");
        });
    }
}

public static void stopUpdates() {
    sub.unsubscribe();
    sub = null;
}
Jeremy Lyman
  • 3,084
  • 1
  • 27
  • 35

1 Answers1

29

Use Observable.interval and to prevent overlapping requests from service.getAthletes() subscribe on a single threaded Scheduler within the flatMap:

Scheduler scheduler = Schedulers.from(Executors.newSingleThreadExecutor());
Observable.interval(x, TimeUnit.SECONDS)
.flatMap(n -> 
    service.getAthletes()
        .retry(3)
        .subscribeOn(scheduler))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<Athlete>>() {
        @Override
        public void call(List<Athlete> athletes) {
            // Handle Success
        }
    }, new Action1<Throwable>() {
        @Override
        public void call(Throwable throwable) {
            // Handle Error
        }
    });
Dave Moten
  • 11,957
  • 2
  • 40
  • 47
  • Can you explain what flatMap is doing here? I'm on Java 7 so lambda are out. Implementing Func1, I notice that it returns an Observable. .subscribe() returns a Subscription. So just return the Observable and it should be fine? – Jeremy Lyman Aug 02 '15 at 03:43
  • 2
    Its how you 'flatten' an Observable> into Observable so yes the Func1 should have signature Func1>>. By the way I would have expected your API to return Observable, not Observable> for greater flexibility (you can get Observable> from service.getAthletes() in that case by just adding .toList if you need it). – Dave Moten Aug 02 '15 at 03:50
  • The service isn't perfect, so instead of an extra model the deserializer strips out just the list of athletes and returns that. Besides that, I noticed that this method will make another call even if the ones before it haven't responded. Is there a way to set this up so that It will only make subsequent calls after the response of the one before it? – Jeremy Lyman Aug 02 '15 at 04:00
  • Perhaps use .delay(x, TimeUnit.SECONDS) and .repeat() ? Something like source.doOnNext(processAthletes).doOnError(logError()).delay(x, TimeUnit.SECONDS).repeat().subscribe(). – Dave Moten Aug 02 '15 at 05:08
  • If you don't want any overlap you could subscribeOn a scheduler built from a single threaded Executor pool using Schedulers.from. I depends a bit on if you always want a delay between service calls. – Dave Moten Aug 02 '15 at 21:58
  • Dave, that is a little above my knowledge of android and schedulers. It looks like this can become a pretty complicated use case. It might be more simple to not use Rx in this case? – Jeremy Lyman Aug 02 '15 at 22:47
  • 1
    Not necessarily. I've updated with single threaded Scheduler use. See what you think. – Dave Moten Aug 03 '15 at 02:26