Paginate Observable results without recursion - RxJava

后端 未结 4 546
天命终不由人
天命终不由人 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:36

    JohnWowUs' answer is great and helped me understand how to avoid the recursion effectively, but there were some points I was still confused about, so I'm posting my tweaked version.

    Summary:

    • The individual pages are returned as a Single.
    • Use a Flowable to stream each of the items contained in the pages. This means callers to our function do not need to know about the individual pages and can just collect the contained items.
    • Use a BehaviorProcessor to start with the first page, and fetch each subsequent page once we have checked with the current page if the next page is available.
    • The key is that the call to processor.onNext(int) starts the next iteration.

    This code depends on rxjava and reactive-streams.

    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Optional;
    import java.util.function.Function;
    
    import io.reactivex.Flowable;
    import io.reactivex.Single;
    import io.reactivex.processors.BehaviorProcessor;
    
    public class Pagination {
    
        // Fetch all pages and return the items contained in those pages, using the provided page fetcher function
        public static  Flowable fetchItems(Function>> fetchPage) {
            // Processor issues page indices
            BehaviorProcessor processor = BehaviorProcessor.createDefault(0);
            // When an index number is issued, fetch the corresponding page
            return processor.concatMap(index -> fetchPage.apply(index).toFlowable())
                            // when returning the page, update the processor to get the next page (or stop)
                            .doOnNext(page -> {
                                if (page.hasNext()) {
                                    processor.onNext(page.getNextPageIndex());
                                } else {
                                    processor.onComplete();
                                }
                            })
                            .concatMapIterable(Page::getElements);
        }
    
        public static void main(String[] args) {
            fetchItems(Pagination::examplePageFetcher).subscribe(System.out::println);
        }
    
        // A function to fetch a page of our paged data
        private static Single> examplePageFetcher(int index) {
            return Single.just(pages.get(index));
        }
    
        // Create some paged data
        private static ArrayList> pages = new ArrayList<>(3);
    
        static {
            pages.add(new Page<>(Arrays.asList("one", "two"), Optional.of(1)));
            pages.add(new Page<>(Arrays.asList("three", "four"), Optional.of(2)));
            pages.add(new Page<>(Arrays.asList("five"), Optional.empty()));
        }
    
        static class Page {
            private List elements;
            private Optional nextPageIndex;
    
            public Page(List elements, Optional nextPageIndex) {
                this.elements = elements;
                this.nextPageIndex = nextPageIndex;
            }
    
            public List getElements() {
                return elements;
            }
    
            public int getNextPageIndex() {
                return nextPageIndex.get();
            }
    
            public boolean hasNext() {
                return nextPageIndex.isPresent();
            }
        }
    }
    

    Output:

    one
    two
    three
    four
    five
    

提交回复
热议问题