Paginate Observable results without recursion - RxJava

后端 未结 4 545
天命终不由人
天命终不由人 2020-12-01 20:09

I\'ve got a pretty standard API pagination problem which you can handle with some simple recursion. Here\'s a fabricated example:

public Observable

        
4条回答
  •  星月不相逢
    2020-12-01 20:38

    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);
        }
    }
    

提交回复
热议问题