AndroidアプリでRetrofit + RxJavaを使用していて、すべてのデータが取得されるまでAPIのページ付けを処理して呼び出しをチェーンする方法について自分自身に尋ねています。次のようなものです。
Observable<ApiResponse> getResults(@Query("page") int page);
ApiResponseオブジェクトの構造は単純です:
class ApiResponse {
int current;
Integer next;
List<ResponseObject> results;
}
APIは、最終ページになるまでnext値を返します。
これを達成するための良い方法はありますか? 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
のストリームが得られ、ほとんどの場合、ページサイズのチャンクで到着します。
Iopar が良い例です。
ほんの少しの追加。
すべてのページを1つのonNext()呼び出しで取得する場合。
この結果をもう1つのObservableで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]
私は同様の投稿で私の解決策に答えました: https://stackoverflow.com/a/34378263/1437
@Ioparによって提供されるソリューションのトリックまたは修正は、さまざまな方法で放出できる「トリガー」Observableを含めることです。
私が投稿したコードでは、要素のページ全体が処理されると出力されますが、ユーザーがボタンやスクロールをクリックしたときにも発生する可能性があります。