使用 RxJava 处理分页

Posted

技术标签:

【中文标题】使用 RxJava 处理分页【英文标题】:Handle Paging with RxJava 【发布时间】:2015-03-18 19:04:55 【问题描述】:

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

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

ApiResponse 对象有一个简单的结构:

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

API 将返回一个 next 值,直到最后一页。

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

【问题讨论】:

你能澄清你的输入和输出吗?带有描述的方法签名会更好。 【参考方案1】:

您可以递归地对其建模:

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 流,它将按顺序到达,并且很可能以页面大小的块的形式到达。

【讨论】:

每天我都会发现一个新的 RxJava 运算符(concatMap 对我来说是新的)。经过测试和批准:) 在这个例子中,有没有办法等待所有结果进来,然后将结果组合成一个可以订阅的响应? toList 可能是最简单的,一旦Observable 完成,它将发出所有元素的单个列表。还有一些其他的运算符用于分组到集合中,例如reduce 请注意,我在另一个答案中对此进行了一些改进:***.com/a/29594194/1424355 用于处理递归可能会破坏堆栈的情况。此外,一般来说,它可能只是一个更简单的替代方案。 “递归可能会破坏堆栈”并且它会破坏。 @lopar 我非常感谢您的解决方案,但是您能否添加有关此解决方案导致严重内存泄漏的信息?如果您将链接添加到另一个答案,那就太好了,因为我一开始没有注意到您的最后评论。【参考方案2】:

我已经在类似的帖子中回答了我的解决方案:https://***.com/a/34378263/143733

@Iopar 提供的解决方案的技巧或修改是包含一个可以通过多种方式发出的“触发器”Observable。

在我发布的代码中,它会在处理完一整页元素后发出,但它也可能基于用户单击按钮/滚动而发生。

【讨论】:

【参考方案3】:

Iopar 举了一个很好的例子。

只是一个小小的补充。 如果您想在一次 onNext() 调用中获取所有页面。 当您想用另一个 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]

【讨论】:

以上是关于使用 RxJava 处理分页的主要内容,如果未能解决你的问题,请参考以下文章

最适合使用RxJava处理的四种场景

RxJava:如何使用 zip 运算符处理错误?

使用 RxJava 延迟获取分页对象

使用 Kotlin 处理错误 RXJava Android

RxJava 错误处理

使用改造对 mvvm 中的 Rxjava 进行错误处理