使用 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]
我在 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]