Я хотел бы знать, как игнорировать исключения и продолжать бесконечный поток (в моем случае поток местоположений)?

Я получаю текущую позицию пользователя (используя Android-ReactiveLocation), а затем отправляю их в свой API (используя Ретрофит ).

В моем случае, когда во время сетевого вызова возникает исключение (например, тайм-аут), вызывается метод 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);
}
53
Ziem 10 Мар 2015 в 20:12

10 ответов

Лучший ответ

mRestService.postLocations(locations) испускает один элемент, затем завершает. Если возникает ошибка, он генерирует ошибку, которая завершает поток.

Когда вы вызываете этот метод в flatMap, ошибка переходит к вашему «основному» потоку, а затем ваш поток останавливается.

Что вы можете сделать, так это преобразовать свою ошибку в другой элемент (как описано здесь: https://stackoverflow.com/a/28971140/476690), но не в основном потоке (как я полагаю, вы уже пробовали), а в mRestService.postLocations(locations).

Таким образом, этот вызов выдаст ошибку, которая будет преобразована в элемент / другой наблюдаемый, а затем завершится. (без вызова onError).

С точки зрения потребителя, mRestService.postLocations(locations) выдаст один элемент, а затем завершится, как если бы все прошло успешно.

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();
24
dwursteisen 19 Апр 2019 в 12:11

Этот ответ может быть немного запоздалым, но если кто-то наткнется на это, вместо того, чтобы изобретать колесо, можно использовать готовую к использованию библиотеку Relay от Джека Уортона.

https://github.com/JakeWharton/RxRelay

Есть хорошая документация, но по сути Relay - это объект, за исключением того, что у него нет возможности вызывать onComplete или onError.

И варианты:

BehaviorRelay

Relay that emits the most recent item it has observed and all subsequent observed items to each subscribed Observer.
    // observer will receive all events.
    BehaviorRelay<Object> relay = BehaviorRelay.createDefault("default");
    relay.subscribe(observer);
    relay.accept("one");
    relay.accept("two");
    relay.accept("three");

    // observer will receive the "one", "two" and "three" events, but not "zero"
    BehaviorRelay<Object> relay = BehaviorRelay.createDefault("default");
    relay.accept("zero");
    relay.accept("one");
    relay.subscribe(observer);
    relay.accept("two");
    relay.accept("three");

PublishRelay Ретранслятор, который после того, как наблюдатель подписался, передает подписчику все последующие наблюдаемые элементы.

    PublishRelay<Object> relay = PublishRelay.create();
    // observer1 will receive all events
    relay.subscribe(observer1);
    relay.accept("one");
    relay.accept("two");
    // observer2 will only receive "three"
    relay.subscribe(observer2);
    relay.accept("three");

ReplayRelay Ретранслятор, который буферизует все элементы, которые он наблюдает, и воспроизводит их любому наблюдателю, который подписывается.

    ReplayRelay<Object> relay = ReplayRelay.create();
    relay.accept("one");
    relay.accept("two");
    relay.accept("three");
    // both of the following will get the events from above
    relay.subscribe(observer1);
    relay.subscribe(observer2);
0
bastami82 2 Окт 2020 в 12:40

С помощью Rxjava2 мы могли бы вызвать перегруженную плоскую карту с параметром delayErrors: плоская карта javadoc

При передаче как истины:

исключения из текущего Flowable и всех внутренних издателей задерживаются до тех пор, пока все они не завершатся, если false, первый сигнализирующий об исключении немедленно завершит всю последовательность

0
YKGrowingUp 4 Июн 2020 в 16:23

Вот моя функция расширения kotlin для игнорирования ошибок

fun <T> Observable<T>.ignoreErrors(errorHandler: (Throwable) -> Unit) =
    retryWhen { errors ->
        errors
            .doOnNext { errorHandler(it) }
            .map { 0 }
    }

Это использует retryWhen для повторной подписки на восходящий поток на неопределенный срок, в то же время позволяя вам использовать метод обработки ошибки нетерминальным способом.

Это опасно

0
William Reed 21 Ноя 2019 в 04:26

Небольшая модификация решения (@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();
            }

        };
    }
}
0
portenez 15 Дек 2015 в 04:10

Добавьте мое решение этой проблемы:

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 5 Июл 2016 в 14:19

Попробуйте вызвать остальную службу в вызове Observable.defer. Таким образом, для каждого вызова у вас будет возможность использовать собственный onErrorResumeNext, и ошибки не приведут к завершению вашего основного потока.

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

Это решение изначально взято из этого потока -> RxJava Observable и Subscriber для пропуска исключения? < / a>, но я думаю, что и в вашем случае это сработает.

1
Community 23 Май 2017 в 12:26

Просто вставьте информацию о ссылке из ответа @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 в цепочке после того, как он выдает исключение, все еще вероятно, что источник будет отписан.

7
AllDayAmazing 4 Ноя 2015 в 21:45

Если вы просто хотите игнорировать ошибку внутри flatMap без возврата элемента , сделайте следующее:

flatMap(item -> 
    restService.getSomething(item).onErrorResumeNext(Observable.empty())
);
15
Albert Vila Calvo 23 Авг 2017 в 06:39

Вы можете использовать один из операторов обработки ошибок.

  • onErrorResumeNext( ) - инструктирует Observable генерировать последовательность элементов, если он обнаруживает ошибку
  • onErrorReturn( ) - указывает Observable выдавать определенный элемент при обнаружении ошибки.
  • onExceptionResumeNext( ) - инструктирует Observable продолжать испускать элементы после того, как он встречает исключение (но не другой вариант throwable)
  • retry( ) - если источник Observable выдает ошибку, повторно подпишитесь на него в надежде, что он завершится без ошибок
  • retryWhen( ) - если источник Observable выдает ошибку, передать эту ошибку другому Observable, чтобы определить, следует ли повторно подписаться на источник

Особенно многообещающе выглядят retry и onExceptionResumeNext в вашем случае.

65
tomrozb 10 Мар 2015 в 18:15