私の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呼び出しを行う
私はどんな解決策やリンクにもとても感謝します
エレガントなネイティブRxJavaソリューションは見つかりません。だからあなたの仕事をするためにSubscriber
をカスタムします。
あなたの3点について:
順次実行の場合、単一のスレッドスケジューラを作成します
Scheduler sequential = Schedulers.from(Executors.newFixedThreadPool(1));
エラーが発生したときにすべてのリクエストを停止するには、毎回Flowable
を作成するのではなく、すべてのリクエストを一緒にサブスクライブする必要があります。したがって、以下の関数を定義します(ここで私が要求するのはInteger
と応答String
です):
void sendRequest(Integer request)
Flowable<String> reciveResponse()
リクエストとレスポンスのフローを関連付けるフィールドを定義します。
FlowableProcessor<Integer> requestQueue = UnicastProcessor.create();
送信されなかったリクエストを再実行するために、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
メソッドを呼び出した後、送信されなかった左のリクエストを引き続き処理します。
この種の動作では、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に変換できます。
お役に立てれば!
私の解決策は次のようになります(前にSwift)で同様のことをしました):
ここではサンプルとして、非同期の監視可能なメソッドを作成することをお勧めします。
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