I\'ve got a pretty standard API pagination problem which you can handle with some simple recursion. Here\'s a fabricated example:
public Observable
Another approach is to use token stream: get data for initial token, push next token to stream once fresh remote data is obtained, and resubscribe until token is empty
public Observable paging() {
Subject tokenStream = BehaviorSubject.create().toSerialized();
tokenStream.onNext(Token.startToken());
Observable dataStream =
Observable.defer(() -> tokenStream.first().flatMap(this::remoteData))
.doOnNext(window -> tokenStream.onNext(window.getToken()))
.repeatWhen(completed -> completed.flatMap(__ -> tokenStream).takeWhile(Token::hasMore));
return dataStream;
}
The result is
Window{next token=Token{key='1'}, data='data for token: Token{key=''}'}
Window{next token=Token{key='2'}, data='data for token: Token{key='1'}'}
Window{next token=Token{key='3'}, data='data for token: Token{key='2'}'}
Window{next token=Token{key='4'}, data='data for token: Token{key='3'}'}
Window{next token=Token{key='5'}, data='data for token: Token{key='4'}'}
Window{next token=Token{key='6'}, data='data for token: Token{key='5'}'}
Window{next token=Token{key='7'}, data='data for token: Token{key='6'}'}
Window{next token=Token{key='8'}, data='data for token: Token{key='7'}'}
Window{next token=Token{key='9'}, data='data for token: Token{key='8'}'}
Window{next token=Token{key='10'}, data='data for token: Token{key='9'}'}
Copy pastable sample
public class RxPaging {
public Observable paging() {
Subject tokenStream = BehaviorSubject.create().toSerialized();
tokenStream.onNext(Token.startToken());
Observable dataStream =
Observable.defer(() -> tokenStream.first().flatMap(this::remoteData))
.doOnNext(window -> tokenStream.onNext(window.getToken()))
.repeatWhen(completed -> completed.flatMap(__ -> tokenStream).takeWhile(Token::hasMore));
return dataStream;
}
private Observable remoteData(Token token) {
/*limit number of pages*/
int page = page(token);
Token nextToken = page < 10
? nextPageToken(token)
: Token.endToken();
return Observable
.just(new Window(nextToken, "data for token: " + token))
.delay(100, TimeUnit.MILLISECONDS);
}
private int page(Token token) {
String key = token.getKey();
return key.isEmpty() ? 0 : Integer.parseInt(key);
}
private Token nextPageToken(Token token) {
String tokenKey = token.getKey();
return tokenKey.isEmpty() ? new Token("1") : nextToken(tokenKey);
}
private Token nextToken(String tokenKey) {
return new Token(String.valueOf(Integer.parseInt(tokenKey) + 1));
}
public static class Token {
private final String key;
private Token(String key) {
this.key = key;
}
public static Token endToken() {
return startToken();
}
public static Token startToken() {
return new Token("");
}
public String getKey() {
return key;
}
public boolean hasMore() {
return !key.isEmpty();
}
@Override
public String toString() {
return "Token{" +
"key='" + key + '\'' +
'}';
}
}
public static class Window {
private final Token token;
private final String data;
public Window(Token token, String data) {
this.token = token;
this.data = data;
}
public Token getToken() {
return token;
}
public String getData() {
return data;
}
@Override
public String toString() {
return "Window{" +
"next token=" + token +
", data='" + data + '\'' +
'}';
}
}
@Test
public void testPaging() throws Exception {
paging().toBlocking().subscribe(System.out::println);
}
}