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