web-dev-qa-db-ja.com

RxJavaでページングを処理する

AndroidアプリでRetrofit + RxJavaを使用していて、すべてのデータが取得されるまでAPIのページ付けを処理して呼び出しをチェーンする方法について自分自身に尋ねています。次のようなものです。

Observable<ApiResponse> getResults(@Query("page") int page);

ApiResponseオブジェクトの構造は単純です:

class ApiResponse {
    int current;
    Integer next;
    List<ResponseObject> results;
}

APIは、最終ページになるまでnext値を返します。

これを達成するための良い方法はありますか? flatMaps()をいくつか組み合わせようとしましたが、成功しませんでした。

28
Rafael Toledo

あなたはそれを再帰的にモデル化することができます:

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のストリームが得られ、ほとんどの場合、ページサイズのチャンクで到着します。

55
lopar

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]
3
Yvgen

私は同様の投稿で私の解決策に答えました: https://stackoverflow.com/a/34378263/1437

@Ioparによって提供されるソリューションのトリックまたは修正は、さまざまな方法で放出できる「トリガー」Observableを含めることです。

私が投稿したコードでは、要素のページ全体が処理されると出力されますが、ユーザーがボタンやスクロールをクリックしたときにも発生する可能性があります。

1
Setheron