web-dev-qa-db-ja.com

RxJava。順次実行

私のAndroidアプリには、ユーザーインタラクションを処理し、一種のリクエストマネージャーを含み、必要に応じてリクエストマネージャーを介してユーザー入力をリクエストマネージャーに送信するプレゼンターがいます。

要求マネージャー自体はサーバーAPIを含み、このRxJavaを使用してサーバー要求を処理します。

ユーザーがメッセージを入力してサーバーからの応答を表示するたびにサーバーにリクエストを送信するコードがあります。

private Observable<List<Answer>> sendRequest(String request) {
    MyRequest request = new MyRequest();
    request.setInput(request);
    return Observable.fromCallable(() -> serverApi.process(request))
            .doOnNext(myResponse -> {
                // store some data
            })
            .map(MyResponse::getAnswers)
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread());
}

しかし、今は一種のキューが必要です。ユーザーは、サーバーが応答する前に新しいメッセージを送信できます。キューからの各メッセージは順次処理されます。つまり2番目のメッセージは、最初のメッセージに対する応答が得られた後に送信されます。

エラーが発生した場合、それ以上のリクエストは処理されません。

また、RecyclerView内に回答を表示する必要があります。

上記のコードを変更して上記の処理を実現する方法がわかりません

何か問題があるようです。一方では、このキューはユーザーがいつでも更新できます。他方では、サーバーが応答を送信するたびに、メッセージをキューから削除する必要があります。

たぶん、rxjavaオペレーターか、私が見逃した特別な方法があります。

ここでも同様の答えを見ましたが、「キュー」は一定です。 RxJavaとRetrofitを使用してNの順次API呼び出しを行う

私はどんな解決策やリンクにもとても感謝します

14
Tima

エレガントなネイティブRxJavaソリューションは見つかりません。だからあなたの仕事をするためにSubscriberをカスタムします。

あなたの3点について:

  1. 順次実行の場合、単一のスレッドスケジューラを作成します

    Scheduler sequential = Schedulers.from(Executors.newFixedThreadPool(1));

  2. エラーが発生したときにすべてのリクエストを停止するには、毎回Flowableを作成するのではなく、すべてのリクエストを一緒にサブスクライブする必要があります。したがって、以下の関数を定義します(ここで私が要求するのはIntegerと応答Stringです):

    void sendRequest(Integer request)

    Flowable<String> reciveResponse()

    リクエストとレスポンスのフローを関連付けるフィールドを定義します。

    FlowableProcessor<Integer> requestQueue = UnicastProcessor.create();

  3. 送信されなかったリクエストを再実行するために、rerun関数を定義します。

    void rerun()

次に、それを使用できます。

reciveResponse().subscribe(/**your subscriber**/)

次に、それらを実装します。

リクエストを送信するときは、requestQueueにプッシュするだけです

public void sendRequest(Integer request) {
  requestQueue.onNext(request);
}

まず、リクエストを順番に実行するには、sequentialに作業をスケジュールする必要があります。

requestQueue
  .observeOn(sequential)
  .map(i -> mockLongTimeRequest(i)) // mock for your serverApi.process
  .observeOn(AndroidSchedulers.mainThread());

次に、エラーが発生したときにリクエストを停止します。これはデフォルトの動作です。何もしない場合、エラーによりサブスクリプションが壊れ、それ以上のアイテムは発行されません。

3番目に、送信されなかったリクエストを再実行します。まず、ネイティブオペレーターがMapSubscriber do(RxJava-2.1.0-FlowableMap#63)のようにストリームをキャンセルするためです。

try {
    v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value.");
} catch (Throwable ex) {
    fail(ex);// fail will call cancel
    return;
}

エラーをラップする必要があります。ここでは、可能性のある例外をラップするために Try クラスを使用しています。例外をスローする代わりに、ラップできる他の実装を使用できます。

    .map(i -> Try.to(() -> mockLongTimeRequest(i)))

そして、それはカスタムですOnErrorStopSubscriber implements Subscriber<Try<T>>, Subscription

通常はアイテムをリクエストして発行します。エラーが発生すると(実際には、失敗したTryが発行されます)、そこで停止し、ダウンストリームリクエストでも要求または発行しません。 rerunメソッドを呼び出した後、実行中のステータスに戻り、通常どおりに発行します。クラスは約80行です。 my github でコードを確認できます。

これでコードをテストできます:

public static void main(String[] args) throws InterruptedException {
  Q47264933 q = new Q47264933();
  IntStream.range(1, 10).forEach(i -> q.sendRequest(i));// emit 1 to 10
  q.reciveResponse().subscribe(e -> System.out.println("\tdo for: " + e));
  Thread.sleep(10000);
  q.rerun(); // re-run after 10s
  Thread.sleep(10000);// wait for it complete because the worker thread is deamon
}

private String mockLongTimeRequest(int i) {
  Thread.sleep((long) (1000 * Math.random()));
  if (i == 5) {
    throw new RuntimeException(); // error occur when request 5
  }
  return Integer.toString(i);
}

および出力:

1 start at:129
1 done  at:948
2 start at:950
    do for: 1
2 done  at:1383
3 start at:1383
    do for: 2
3 done  at:1778
4 start at:1778
    do for: 3
4 done  at:2397
5 start at:2397
    do for: 4
error happen: Java.lang.RuntimeException
6 start at:10129
6 done  at:10253
7 start at:10253
    do for: 6
7 done  at:10415
8 start at:10415
    do for: 7
8 done  at:10874
9 start at:10874
    do for: 8
9 done  at:11544
    do for: 9

順次実行されていることがわかります。エラー発生時に停止しました。 rerunメソッドを呼び出した後、送信されなかった左のリクエストを引き続き処理します。

完全なコードについては、私のgithubを参照してください。

8
Dean Xu

この種の動作では、Flowableバックプレッシャーの実装を使用しています。 APIリクエストストリームの親である外部ストリームを作成し、maxConcurrency = 1でAPIリクエストをflatMapし、なんらかのバッファ戦略を実装して、Flowableが例外をスローしないようにします。

_Flowable.create(emitter -> {/* user input stream*/}, BackpressureStrategy.BUFFER)
                .onBackpressureBuffer(127, // buffer size
                        () -> {/* overflow action*/},
                        BackpressureOverflowStrategy.DROP_LATEST) // action when buffer exceeds 127
                .flatMap(request -> sendRequest(request), 1) // very important parameter
                .subscribe(results -> {
                    // work with results
                }, error -> {
                    // work with errors
                });
_

ユーザー入力を所定のしきい値までバッファーしてからドロップします(これを行わないと、例外がスローされますが、ユーザーがそのようなバッファーを超える可能性はほとんどありません)。キューのように1つずつ順番に実行されます。ライブラリ自体にある種の振る舞いの演算子がある場合は、この振る舞いを自分で実装しようとしないでください。

ああ、私は言及するのを忘れていました。あなたのsendRequest()メソッドはFlowableを返さなければなりません。そうでなければ、それをFlowableに変換できます。

お役に立てれば!

2
Tuby

私の解決策は次のようになります(前にSwift)で同様のことをしました):

  1. リクエストとレスポンスの両方にラッパーインターフェース(「Event」と呼びましょう)が必要です。
  2. 要求キューと最新のサーバー応答を含む状態オブジェクト(クラスを「状態」にしましょう)と、パラメーターとして「イベント」を受け入れて「this」を返すメソッドが必要です。
  3. メインの処理チェーンは次のようになりますObservable state = Observable.merge(serverResponsesMappedToEventObservable、requestsMappedToEventObservable).scan(new State()、(state、event)-> {state.apply(event)})
  4. 。merge()メソッドの両方のパラメーターは、おそらくサブジェクトになります。
  5. キュー処理は「State」オブジェクトの唯一のメソッドで発生します(任意のイベントでキューから要求を選択して送信し、要求イベントでキューに追加し、応答イベントで最新の応答を更新します)。
1
Maxim Volgin

ここではサンプルとして、非同期の監視可能なメソッドを作成することをお勧めします。

public Observable<Integer> sendRequest(int x){
    return Observable.defer(() -> {
        System.out.println("Sending Request : you get Here X ");
        return storeYourData(x);
    });
}

public Observable<Integer> storeYourData(int x){
    return Observable.defer(() -> {
        System.out.println("X Stored : "+x);
        return readAnswers(x);
    }).doOnError(this::handlingStoreErrors);
}

public Observable<Integer> readAnswers(int h){
    return Observable.just(h);
}

public void handlingStoreErrors(Throwable throwable){
        //Handle Your Exception.
}

最初のオブザーバブルは、応答を取得したときにリクエストを送信し、2番目のオブザーバーが続行すると、チェーンを作成できます。各メソッドをカスタマイズして、エラーや成功を処理できます。このサンプルはキューのようです。

ここで実行結果:

for (int i = 0; i < 1000; i++) {
        rx.sendRequest(i).subscribe(integer -> System.out.println(integer));

}
Sending Request : you get Here X 
X Stored : 0
0
Sending Request : you get Here X 
X Stored : 1
1
Sending Request : you get Here X 
X Stored : 2
2
Sending Request : you get Here X 
X Stored : 3
3
.
.
.
Sending Request : you get Here X 
X Stored : 996
996
Sending Request : you get Here X 
X Stored : 997
997
Sending Request : you get Here X 
X Stored : 998
998
Sending Request : you get Here X 
X Stored : 999
999
1
Elyes