Обработка пейджинга с помощью RxJava
Я использую Retrofit + RxJava в приложении для Android и задаю себе вопрос о том, как обрабатывать разбиение на страницы 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
, который поступит по порядку и, скорее всего, придет в куски размера страницы.
Ответ 2
Я ответил на свое решение в аналогичном сообщении: fooobar.com/questions/251142/...
Трюк или поправка к решению, предоставленному @Iopar, - это включение "триггера", наблюдаемого, которое может излучаться различными способами.
В опубликованном мной коде он выдается после того, как была обработана полная страница элементов, но также может произойти на основе нажатия пользователем кнопки/прокрутки.
Ответ 3
Iopar дал отличный пример.
Просто небольшое дополнение.
Если вы хотите получить все страницы в одном вызове onNext().
Это может быть полезно, когда вы хотите zip получить этот результат еще одним наблюдаемым.
Вы должны написать:
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]