例外を無視し、無限ストリーム(私の場合は場所のストリーム)を継続する方法を知りたいですか?
現在のユーザーの位置を取得し( Android-ReactiveLocation を使用)、それらをAPIに送信しています( Retrofit を使用)。
私の場合、ネットワーク呼び出し中に例外が発生すると(タイムアウトなど)、onError
メソッドが呼び出され、ストリーム自体が停止します。それを避ける方法は?
アクティビティ:
private RestService mRestService;
private Subscription mSubscription;
private LocationRequest mLocationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(100);
...
private void start() {
mRestService = ...;
ReactiveLocationProvider reactiveLocationProvider = new ReactiveLocationProvider(this);
mSubscription = reactiveLocationProvider.getUpdatedLocation(mLocationRequest)
.buffer(50)
.flatMap(locations -> mRestService.postLocations(locations)) // can throw exception
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
}
RestService:
public interface RestService {
@POST("/.../")
Observable<Response> postLocations(@Body List<Location> locations);
}
mRestService.postLocations(locations)
は1つのアイテムを発行し、完了します。エラーが発生すると、エラーが発生し、ストリームが完了します。
flatMap
でこのメソッドを呼び出すと、エラーが「メイン」ストリームに続き、ストリームが停止します。
あなたができることは、エラーを別のアイテムに変換することです(ここで説明されているように: https://stackoverflow.com/a/28971140/47669 )が、メインストリームではありません(あなたが推測するように)既に試しました)が、mRestService.postLocations(locations)
で。
この方法では、この呼び出しはエラーを発し、アイテム/別のオブザーバブルに変換されてから完了します。 (onError
を呼び出さずに)。
コンシューマービューでは、mRestService.postLocations(locations)
は1つのアイテムを発行し、すべてが成功した場合と同様に完了します。
mSubscription = reactiveLocationProvider.getUpdatedLocation(mLocationRequest)
.buffer(50)
.flatMap(locations -> mRestService.postLocations(locations).onErrorReturn((e) -> Collections.emptyList()) // can't throw exception
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
エラー処理演算子 のいずれかを使用できます。
onErrorResumeNext( )
— Observableにエラーが発生した場合にアイテムのシーケンスを発行するよう指示しますonErrorReturn( )
—エラーが発生したときに特定のアイテムを発行するようにObservableに指示しますonExceptionResumeNext( )
—例外に遭遇した後もアイテムを出力し続けるようにObservableに指示します(ただし、他の種類のスロー可能オブジェクトは例外ではありません)retry( )
—ソースObservableがエラーを出力する場合、エラーなしで完了することを期待して再サブスクライブしますretryWhen( )
—ソースObservableがエラーを出力する場合、そのエラーを別のObservableに渡して、ソースを再サブスクライブするかどうかを決定します特にretry
とonExceptionResumeNext
はあなたの場合に有望に見えます。
flatMap
内のエラーを単に無視したい場合要素を返さずにこれを行います:
flatMap(item ->
restService.getSomething(item).onErrorResumeNext(Observable.empty())
);
@MikeNの回答からリンク情報を貼り付けて、それが失われた場合に備えてください。
import rx.Observable.Operator;
import rx.functions.Action1;
public final class OperatorSuppressError<T> implements Operator<T, T> {
final Action1<Throwable> onError;
public OperatorSuppressError(Action1<Throwable> onError) {
this.onError = onError;
}
@Override
public Subscriber<? super T> call(final Subscriber<? super T> t1) {
return new Subscriber<T>(t1) {
@Override
public void onNext(T t) {
t1.onNext(t);
}
@Override
public void onError(Throwable e) {
onError.call(e);
}
@Override
public void onCompleted() {
t1.onCompleted();
}
};
}
}
他のオペレーターはその前に熱心に退会する可能性があるため、観察可能なソースの近くで使用します。
Observerable.create(connectToUnboundedStream()).lift(new OperatorSuppressError(log()).doOnNext(someStuff()).subscribe();
ただし、これにより、ソースからのエラー配信が抑制されることに注意してください。チェーンに例外がスローされた後のonNextが存在する場合、それでもソースはサブスクライブ解除される可能性があります。
Observable.defer呼び出しで残りのサービスを呼び出してみてください。この方法では、呼び出しごとに独自の 'onErrorResumeNext'を使用する機会が得られ、エラーによってメインストリームが完了しません。
reactiveLocationProvider.getUpdatedLocation(mLocationRequest)
.buffer(50)
.flatMap(locations ->
Observable.defer(() -> mRestService.postLocations(locations))
.onErrorResumeNext(<SOME_DEFAULT_TO_REACT_TO>)
)
........
その解決策はもともとこのスレッドからのものです-> RxJava Observable and Subscriber for skip skip exception? ですが、あなたの場合でもうまくいくと思います。
この問題の解決策を追加します。
privider
.compose(ignoreErrorsTransformer)
.subscribe()
private final Observable.Transformer<ResultType, ResultType> ignoreErrorsTransformer =
new Observable.Transformer<ResultType, ResultType>() {
@Override
public Observable<ResultType> call(Observable<ResultType> resultTypeObservable) {
return resultTypeObservable
.materialize()
.filter(new Func1<Notification<ResultType>, Boolean>() {
@Override
public Boolean call(Notification<ResultType> resultTypeNotification) {
return !resultTypeNotification.isOnError();
}
})
.dematerialize();
}
};
有限ストリームを完了するためのソリューション(@MikeN)のわずかな変更:
import rx.Observable.Operator;
import rx.functions.Action1;
public final class OperatorSuppressError<T> implements Operator<T, T> {
final Action1<Throwable> onError;
public OperatorSuppressError(Action1<Throwable> onError) {
this.onError = onError;
}
@Override
public Subscriber<? super T> call(final Subscriber<? super T> t1) {
return new Subscriber<T>(t1) {
@Override
public void onNext(T t) {
t1.onNext(t);
}
@Override
public void onError(Throwable e) {
onError.call(e);
//this will allow finite streams to complete
t1.onCompleted();
}
@Override
public void onCompleted() {
t1.onCompleted();
}
};
}
}