Rx java 如何使用RxJava处理分页?

Rx java 如何使用RxJava处理分页?,rx-java,Rx Java,我正在考虑将我的android应用程序转换为使用Rxjava进行网络请求。我当前访问的Web服务类似于: getUsersByKeyword(String query, int limit, int offset) 据我所知,可观测对象是“推”而不是“拉”接口。以下是我对解决问题的理解: 应用程序向服务注册,可用于查询 结果被推送到应用程序 应用程序处理结果 当应用程序需要更多结果时 这就是我的问题所在。之前,我只需要向webservice询问我想要的内容,然后使用偏移量再次进行查询。但在

我正在考虑将我的android应用程序转换为使用Rxjava进行网络请求。我当前访问的Web服务类似于:

getUsersByKeyword(String query, int limit, int offset)
据我所知,可观测对象是“推”而不是“拉”接口。以下是我对解决问题的理解:

  • 应用程序向服务注册,可用于查询
  • 结果被推送到应用程序
  • 应用程序处理结果
  • 当应用程序需要更多结果时
这就是我的问题所在。之前,我只需要向webservice询问我想要的内容,然后使用偏移量再次进行查询。但在这种情况下,这将涉及到创建另一个可观察对象并订阅它,有点违背了这一点


我应该如何处理我的应用程序中的分页?(这是一款android应用程序,但我认为这与此无关)。

我已经完成了这项工作,实际上并不难

该方法是对FirstRequestsAbservable中的每个第一个请求(偏移量0)进行建模。为了方便起见,您可以将其设置为PublishSubject,在其中调用
onNext()
,以输入下一个请求,但有更智能的非主题方式(例如,如果在单击按钮时完成请求,那么requestObservable就是通过某些操作符映射的clickObservable)

一旦
firstRequestsObservable
就位,您就可以通过从
firstRequestsObservable
等进行平面映射来进行
responseObservable
服务调用

现在,诀窍来了:创建另一个名为
subsequentRequestsObservable
的可观察对象,该对象从
responseObservable
映射,增加偏移量(为此,最好在响应数据中包含原始请求的偏移量)。一旦引入此可观察项,您现在必须更改
responseObservable
的定义,以便它也依赖于
后续的questsobservable
。然后会得到如下循环依赖项:

FirstRequestsSobservable->responseObservable->SubsequentRequestsSobservable->responseObservable->SubsequentRequestsSobservable->

若要打破此循环,您可能希望在
subsequentRequestsBServable
的定义中包含
过滤器
运算符,过滤掉偏移量将超过“总计”限制的情况。循环依赖还意味着您需要将其中一个作为主题,否则就不可能声明可观察对象。我建议将
responseObservable
作为该主题


总之,首先将responseObservable初始化为主题,然后声明firstRequestsObservable,然后声明随后的questsobservable,作为通过一些操作符传递responseObservable的结果。然后,可以使用
onNext

将responseObservable“输入”,因此,如果这是单向分页,您可以尝试以下模式。这段代码还没有运行或编译,但我已经尝试过过度注释以解释发生了什么

private static final int LIMIT=50;
//给定:返回一个虚拟事件对象流,告诉我们何时
//抓取下一页。这可能是通过点击或任何地方。
可观察的getNextPageEvents();
//鉴于:
//搜索查询关键字。这里的每一次排放都意味着一个新的顶层
//请求;
可观察的查询;
querys.switchMap((query)->getNextPageEvents()
//无法获取时忽略“下一页”戳。
.onBackPressureDrop()
//用第一页种子。
.startWith(新的NextPageEvent())
//增加每个请求的页码。
.扫描(0,(第页,事件)->第+1页)
//从服务器请求页面。
.concatMap((第页)->getUsersByKeyword(查询、限制、限制*第页)
//展开可观察的可观察的。从(用户列表))
.retryWhen(/**在此处插入您最喜欢的重试逻辑。*/)
//仅按顺序处理新页面请求。
.scheduleOn(Schedulers.trampoline())
//“当前线程”上的蹦床时间表,因此我们确保
//背景IO线程。
.scheduleOn(Schedulers.io());

这应该允许“下一页事件”信号每次触发下一页数据的加载,并且在加载错误时不会跳页。如果收到新的搜索查询,它也会在顶层完全重新启动。如果我(或其他人?)有时间,我想检查我对蹦床和背压的假设,并确保它阻止任何在加载时过早获取下一页的尝试。

说清楚,我想你的问题更多的是如何在Android应用程序中应用RxJava,而不是在后端(虽然也可以应用,但不像前端那样典型)。 我不确定这个例子是否是使用反应式函数编程(RFP)模型的典型用例,除了干净的代码构造

下面的每一条流都是可以观察到的。就我个人而言,我认为这是一条流,因为很容易对事件进行推理。 分页流可以由6个流表示:

firstPageStream -f-------------------> // this is actually to produce very first event to obtain the first page result. The event can come from a touch in one of the screen that navigates to this list screen.
nextPageStream  -----n-------n-------> // this is the source of events coming from Next button 'touch' actions
prevPageStream  ---------p-------p---> // this is the source of events coming from Previous button 'touch' actions
requestStream   -r---r---r---r---r---> // this is to consume the signal from 3 streams above and spawn events (r) which create a query and pagination details i.e.: offset and limit
responseStream  -R---R---R---R---R---> // this is to take each r and invoke your web service getUsersByKeyword() then spawn the response (R)
上面的流可以用下面的伪代码(JS风格)表示(翻译成RxJava或其他语言相当容易)

PS1:我还没有测试过上面的代码。我正在学习RFP,所以我试着通过写下来以反应的方式思考。欢迎任何建议

PS2:对于解释反应流的方法,我深受其影响。

这是硬石!) 因此,我们对网络提出了请求:
getUsersByKeyword(字符串查询、整数限制、整数偏移量)

例如,此请求返回
列表

如果我们使用网络改造,该请求将如下所示:
Observable>getUsersByKeyword(字符串查询、整数限制、整数偏移量)

因此,我们想要通用电气
firstPageStream = Observable.just(0); // offset=0
nextPageStream = Observable.fromEvent(nextButton, 'touch')
                           .map(function() {
                              offset = offset + limit;
                              return offset;
                            });
prevPageStream = Observable.fromEvent(prevButton, 'touch')
                           .map(function() {
                              offset = offset - limit; // TODO some check here
                              return offset;
                            });
requestStream = Observable.merge(firstPageStream, nextPageStream, prevPageStream)
                            .map(function(offsetValue) {
                               return {offset : offsetValue, limit: limit};
                            });
responseStream = requestStream.flatMap(function(pagination) {
                                return webservice.getUsersByKeyword(query, pagination.offset, pagination.limit); // assume this is async response
                              });
responseStream.subscribe(function(result) {
  // use result to render the display
});
int page = 50;
int limit = page;
Observable
                .range(0, Integer.MAX_VALUE - 1)
                .concatMap(new Func1<Integer, Observable<List<Result>>>() {
                    @Override
                    public Observable<List<Result>> call(Integer integer) {
                        return getUsersByKeyword(query, integer * page, limit);
                    }
                })
                .takeWhile(new Func1<List<Result>, Boolean>() {
                    @Override
                    public Boolean call(List<Result> results) {
                        return !results.isEmpty();
                    }
                })
                .scan(new Func2< List<Result>, List<Result>, List<Result>>() {
                    @Override
                    public List<Result> call(List<Result> results, List< Result> results2) {
                        List<Result> list = new ArrayList<>();
                        list.addAll(results);
                        list.addAll(results2);
                        return list;
                    }
                })
                .last()
                .subscribe(new Subscriber<List<Result>>() {
                    @Override
                    public void onCompleted() {
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onNext(List<Results> results) {
                    }
                });