使用 RxJava 处理分页

Handle Paging with RxJava

我在 Android 应用程序上使用 Retrofit + RxJava,我在问自己如何处理 API 分页以链接调用,直到检索到所有数据。是不是这样:

Observable<ApiResponse> getResults(@Query("page") int page);

ApiResponse 对象具有简单的结构:

class ApiResponse {
    int current;
    Integer next;
    List<ResponseObject> results;
}

API 将 return 一个 下一个 值,直到最后一页。

有什么好的方法可以做到这一点?尝试合并一些 flatMaps(),但没有成功。

您可以对其进行递归建模:

Observable<ApiResponse> getPageAndNext(int page) {
  return getResults(page)
      .concatMap(new Func1<ApiResponse, Observable<ApiResponse>>() {

        @Override
        public Observable<ApiResponse> call(ApiResponse response) {
          // Terminal case.
          if (response.next == null) {
            return Observable.just(response);
          }
          return Observable.just(response)
              .concatWith(getPageAndNext(response.next));
        }

      });
}

然后,消费它,

getPageAndNext(0)
    .concatMap(new Func1<ApiResponse, Observable<ResponseObject>>() {

        @Override
        public Observable<ResponseObject> call(ApiResponse response) {
          return Observable.from(response.results);
        }

    })
    .subscribe(new Action1<ResponseObject>() { /** Do something with it */ });

这应该会为您提供一个 ResponseObject 流,该流将按顺序到达,并且很可能以页面大小的块到达。

我已经用类似的方式回答了我的解决方案 post:

@Iopar 提供的解决方案的技巧或修正是包含一个 'trigger' 可以通过多种方式发出的 Observable。

在我 post 编写的代码中,它会在处理完一整页元素后发出,但它也可能基于用户单击 button/scrolling。

Iopar举了一个很好的例子。

只是一个小补充。
如果你想在一次 onNext() 调用中获取所有页面。
当你想 zip 这个结果和一个 Observable 时,它​​会很有帮助。
你应该写:

private List<String> list = new LinkedList() {
    {
        add("a");
        add("b");
        add("c");
    }
};

int count = 1;

public Observable<List<String>> getAllStrings(int c) {
    return Observable.just(list)
            .concatMap(
                    strings -> {
                        if (c == 3) {
                            return Observable.just(list);
                        } else {
                            count += 1;
                            return Observable.zip(
                                    Observable.just(list),
                                    getAllStrings(count),
                                    (strings1, strings2) -> {
                                        strings1.addAll(strings2);
                                        return strings1;
                                    }
                            );
                        }
                    }
            );
}

用法:

getAllStrings(0)
        .subscribe(strings -> {
            Log.w(TAG, "call: " + strings);
        });

你将得到:

call: [a, b, c, a, b, c, a, b, c, a, b, c]