我有一个用例,我想反复为每个用户调用一个Web API,直到下载完整个数据。
我拥有的Web API允许每个API请求为用户提取最多100条记录。我可以指定要为该用户下载的记录的startTime
和结束时间戳:
void downloadRecord(String userId, recordStartTime, recordEndTime, int countOfRecord, ResultCallBack)
如果recordStartTime
和recordEndTime
之间的记录数超过100,则API响应仅返回100条记录。然后我必须通过新的开始时间(刚刚下载的第100条记录的时间)循环调用此api,直到下载所有记录。
final long recordStartTime = timeStampFrom2DaysAgo//;
Observable.from(arrayListOfUserIds).flatMap(new Func1<String, Observable<?>>() {
@Override
public Observable<?> call(String userId) {
long recordEndTime = getCurrentTimeMS();
//keep downloading records
downloadRecord(userId, recordStartTime, recordEndTime, 100, new ResultCallBack() {
//if records are < 100 then stop
//else keep downloading
});
}
}).subscribe();
请建议我是否有可用于解决问题的RxJava示例代码。
由于
答案 0 :(得分:4)
这是一个循环相关的Observables。想象一下,你有一个Observable用于请求,一个Observable用于响应。 requests
Observable发出一些DownloadParams
个对象,包含userId,recordStartTime,recordEndTime和countOfRecord,因此Observable<DownloadParams> requests
。 responses
Observable会发出记录列表,即Observable<List<Record>> responses
。
responses
显然取决于requests
,但不那么明显的部分是我们还需要requests
依赖responses
,因为下次从服务器下载,某些DownloadParams
取决于我们从之前的下载中获得的响应。为了完整性,requests
实际上还取决于一些初始化Observable,它发出userId来执行第一次下载。您可以使用.startWith(firstDownloadParams)
替换此初始化Observable。
无论如何,困难的部分是表达循环依赖。好消息是这在Rx中是可能的,并且已经the focus of Cycle.js framework based on RxJS。坏消息是,如果没有主题,我们就无法解决这个问题,这可能是不可取的。
最好让RxJava代码保持尽可能的功能,但通过尝试这样做我们就会遇到问题。如果我们尝试将Observable声明为其他人的函数,我们得到:
Observable<DownloadParams> requests = responses.flatMap( /* ... */ )
.startWith(firstDownloadParams);
Observable<List<Record>> responses = requests.flatMap( /* ... */ );
这不会编译,因为第一个声明需要第二个声明,反之亦然。这是受试者可以提供的帮助。我们将其中一个Observable声明为主题:
PublishSubject<List<Record>> responsesProxy = PublishSubject.create();
Observable<DownloadParams> requests = responsesProxy.flatMap( /* ... */ )
.startWith(firstDownloadParams);
Observable<List<Record>> responses = requests.flatMap( /* ... */ );
主题responsesProxy
将充当responses
的代理,以便我们能够根据requests
声明responsesProxy
。但现在我们需要responsesProxy
来模仿responses
。我们通过添加以下内容来实现:
responses.subscribe(responsesProxy);
这会关闭循环依赖中的循环,但只记得在我们上面的订阅之后也正确处理了主题。
现在我们只需填写那些flatMap
中的转换。在requests.flatMap( )
中,您应该执行下载记录列表的网络调用。您可能希望将其作为Observable处理,而不是作为回调处理,以简化与其余RxJava代码的相互作用。
在responsesProxy.flatMap( )
中,您应该检查记录列表是否为100或更多,并使用新的recordStartTime创建下一个DownloadParams
,并将其包装为Observable.just(newDownloadParams)
。如果小于100,则返回Observable.empty()
。
答案 1 :(得分:2)
这可能会变得复杂:请参阅示例gist here。
基本上你需要踩踏有关后续查询时间窗口的请求。请注意,BufferUntilSubscriber
是内部的,但RxJava没有任何正式的Subject
变体,它会在订阅发生之前缓存值,然后丢弃缓存。
修改:更新了要点,因此它不会触发MissinBackpressureException
。