22

My problem is i can't get infinite stream with Retrofit. After i get credentials for initial poll() request - i do initial poll() request. Each poll() request responds in 25 sec if there is no change, or earlier if there are any changes - returning changed_data[]. Each response contains timestamp data needed for next poll request - i should do new poll() request after each poll() response. Here is my code:

getServerApi().getLongPollServer() 
  .flatMap(longPollServer -> getLongPollServerApi(longPollServer.getServer()).poll("a_check", Config.LONG_POLLING_SERVER_TIMEOUT, 2, longPollServer.getKey(), longPollServer.getTs(), "") 
   .take(1) 
   .flatMap(longPollEnvelope -> getLongPollServerApi(longPollServer.getServer()).poll("a_check", Config.LONG_POLLING_SERVER_TIMEOUT, 2, longPollServer.getKey(), longPollEnvelope.getTs(), ""))) 
   .retry()
   .subscribe(longPollEnvelope1 -> {
   processUpdates(longPollEnvelope1.getUpdates());
});

I'm new to RxJava, maybe i don't understand something, but i can't get infinite stream. I get 3 calls, then onNext and onComplete.

P.S. Maybe there is a better solution to implement long-polling on Android?

ericn
  • 12,476
  • 16
  • 84
  • 127
localhost
  • 5,568
  • 1
  • 33
  • 53

1 Answers1

12

Whilst not ideal, I believe that you could use RX's side effects to achieve a desired result ('doOn' operations).

Observable<CredentialsWithTimestamp> credentialsProvider = Observable.just(new CredentialsWithTimestamp("credentials", 1434873025320L)); // replace with your implementation

Observable<ServerResponse> o = credentialsProvider.flatMap(credentialsWithTimestamp -> {
    // side effect variable
    AtomicLong timestamp = new AtomicLong(credentialsWithTimestamp.timestamp); // computational steering (inc. initial value)
    return Observable.just(credentialsWithTimestamp.credentials) // same credentials are reused for each request - if invalid / onError, the later retry() will be called for new credentials
            .flatMap(credentials -> api.query("request", credentials, timestamp.get()))  // this will use the value from previous doOnNext
            .doOnNext(serverResponse -> timestamp.set(serverResponse.getTimestamp()))
            .repeat();
})
        .retry()
        .share();

private static class CredentialsWithTimestamp {

    public final String credentials;
    public final long timestamp; // I assume this is necessary for you from the first request

    public CredentialsWithTimestamp(String credentials, long timestamp) {
        this.credentials = credentials;
        this.timestamp = timestamp;
    }
}

When subscribing to 'o' the internal observable will repeat. Should there be an error then 'o' will retry and re-request from the credentials stream.

In your example, computational steering is achieved by updating the timestamp variable, which is necessary for the next request.

snodnipper
  • 2,580
  • 1
  • 29
  • 19
  • thank for your answer. However, i get a timestamp from api and i should send it back with a new poll() call. – localhost Jun 20 '15 at 22:41
  • I have updated the answer to hopefully closer match your situation. You can see that when you get a serverResponse, you are simply setting a variable. "doOnNext" makes the side effect explicit. My concern is that this isn't pretty and we'd need to see your code to make a better answer. – snodnipper Jun 21 '15 at 08:27
  • I have similar issue and resolved using your code but in my case I want to store value too at very first time.Where I can put that code? – Krutik Mar 30 '17 at 06:40
  • @Krutik - you can subscribe to "o" a second time and use the [first](http://reactivex.io/documentation/operators/first.html) operator. If you need further explanation then consider creating another question - defaults, error conditions and the share() behaviour may be a consideration. – snodnipper Mar 30 '17 at 12:08