web-dev-qa-db-fra.com

Comment ignorer l'erreur et continuer le flux infini?

Je voudrais savoir comment ignorer les exceptions et continuer le flux infini (dans mon cas, le flux d'emplacements)?

Je récupère la position actuelle de l'utilisateur (en utilisant Android-ReactiveLocation ), puis je les envoie à mon API (en utilisant Retrofit ).

Dans mon cas, lorsqu'une exception se produit pendant un appel réseau (par exemple timeout), la méthode onError est invoquée et le flux s'arrête. Comment l'éviter?

Activité:

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);
}
38
Ziem

mRestService.postLocations(locations) émettez un élément, puis terminez. Si une erreur se produit, il émet alors l'erreur, ce qui termine le flux.

Lorsque vous appelez cette méthode dans un flatMap, l'erreur continue dans votre flux "principal", puis votre flux s'arrête.

Ce que vous pouvez faire est de transformer votre erreur en un autre élément (comme décrit ici: https://stackoverflow.com/a/28971140/47669 ), mais pas sur votre flux principal (comme je vous présume déjà essayé) mais sur la mRestService.postLocations(locations).

De cette façon, cet appel émettra une erreur, qui sera transformée en un élément/un autre observable puis complétée. (sans appeler onError).

Dans une vue grand public, mRestService.postLocations(locations) émettra un élément, puis se terminera, comme si tout réussissait.

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();
15
dwursteisen

Vous pouvez utiliser l'un des opérateurs de gestion des erreurs .

  • onErrorResumeNext( ) - demande à un observable d'émettre une séquence d'éléments s'il rencontre une erreur
  • onErrorReturn( ) - demande à un observable d'émettre un élément particulier lorsqu'il rencontre une erreur
  • onExceptionResumeNext( ) - indique à un observable de continuer à émettre des éléments après avoir rencontré une exception (mais pas une autre variété de jetables)
  • retry( ) - si une source Observable émet une erreur, réabonnez-vous en espérant qu'elle se terminera sans erreur
  • retryWhen( ) - si un observable source émet une erreur, transmettez cette erreur à un autre observable pour déterminer s'il doit se réinscrire à la source

Surtout retry et onExceptionResumeNext semblent prometteurs dans votre cas.

51
tomrozb

Si vous voulez simplement ignorer l'erreur à l'intérieur de flatMapsans renvoyer d'élément faites ceci:

flatMap(item -> 
    restService.getSomething(item).onErrorResumeNext(Observable.empty())
);
9
Albert Vila Calvo

Le simple fait de coller les informations de lien de la réponse de @ MikeN au cas où il serait perdu:

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();
            }

        };
    }
}

et l'utiliser près de la source observable car d'autres opérateurs peuvent se désinscrire avec impatience avant cela.

Observerable.create(connectToUnboundedStream()).lift(new OperatorSuppressError(log()).doOnNext(someStuff()).subscribe();

Notez, cependant, que cela supprime la livraison d'erreur à partir de la source. Si un onNext de la chaîne après avoir levé une exception, il est probable que la source sera désabonnée.

6
AllDayAmazing

Essayez d'appeler le service de repos dans un appel Observable.defer. De cette façon, pour chaque appel, vous aurez la possibilité d'utiliser son propre "onErrorResumeNext" et les erreurs n'entraîneront pas la fin de votre flux principal.

reactiveLocationProvider.getUpdatedLocation(mLocationRequest)
  .buffer(50)
  .flatMap(locations ->
    Observable.defer(() -> mRestService.postLocations(locations))
      .onErrorResumeNext(<SOME_DEFAULT_TO_REACT_TO>)
  )
........

Cette solution est à l'origine de ce fil -> RxJava observable et abonné pour ignorer l'exception? , mais je pense que cela fonctionnera aussi dans votre cas.

1
meddle

Ajoutez ma solution à ce problème:

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();

            }
        };
1
HotIceCream

Une légère modification de la solution (@MikeN) pour permettre aux flux finis de se terminer:

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();
            }

        };
    }
}
0
portenez