如何忽略错误并继续无限流?

时间:2015-03-10 17:13:00

标签: android rx-java rx-android

我想知道如何忽略异常并继续无限流(在我的情况下是位置流)?

我获取当前用户位置(使用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);
}

11 个答案:

答案 0 :(得分:49)

您可能想要使用其中一个error handling operators

  • onErrorResumeNext( ) - 指示Observable在遇到错误时发出一系列项目
  • onErrorReturn( ) - 指示Observable在遇到错误时发出特定项目
  • onExceptionResumeNext( ) - 指示Observable在遇到异常后继续发出项目(但不是其他类型的throwable)
  • retry( ) - 如果来源Observable发出错误,请重新订阅,希望它能完整而不会出错
  • retryWhen( ) - 如果源Observable发出错误,请将该错误传递给另一个Observable以确定是否重新订阅源

Especialy retryonExceptionResumeNext看起来很有希望。

答案 1 :(得分:13)

mRestService.postLocations(locations)发出一个项目,然后完成。 如果发生错误,则会发出错误,从而完成流。

当您在flatMap中调用此方法时,错误将继续显示在&#34; main&#34;流,然后您的流停止。

您可以做的是将您的错误转换为另一个项目(如此处所述:https://stackoverflow.com/a/28971140/476690),但不在主流上(我认为您已尝试过)但在mRestService.postLocations(locations)上。

这样,此调用将发出错误,该错误将转换为项目/另一个observable,然后完成。 (不调用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();

答案 2 :(得分:6)

只需粘贴来自@ 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,则为   仍有可能取消订阅来源。

答案 3 :(得分:6)

如果您只想忽略flatMap 中的错误而不返回元素,请执行以下操作:

flatMap(item -> 
    restService.getSomething(item).onErrorResumeNext(Observable.empty())
);

答案 4 :(得分:1)

尝试在Observable.defer调用中调用rest服务。这样,对于每次调用,您都有机会使用自己的'onErrorResumeNext'并且错误不会导致主流完成。

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

该解决方案最初来自此主题 - &gt; RxJava Observable and Subscriber for skipping exception?,但我认为它也适用于你的情况。

答案 5 :(得分:1)

为此问题添加我的解决方案:

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

            }
        };

答案 6 :(得分:0)

稍微修改解决方案(@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();
            }

        };
    }
}

答案 7 :(得分:0)

这是我的Kotlin扩展函数,用于忽略错误

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

这利用retryWhen无限期地重新订阅上游,同时仍然允许您以非终端方式处理错误。

这很危险

答案 8 :(得分:0)

使用Rxjava2,我们可以使用delayErrors参数调用重载的平面图: flatmap javadoc

将其正确传递时:

当前Flowable和所有内部发布者的异常被延迟,直到所有它们都为false终止为止,第一个发出异常信号将立即终止整个序列

答案 9 :(得分:0)

这个答案可能有点晚了,但是如果有人偶然发现了这个问题,那么不用重新发明轮子,而是可以使用Jacke Wharton的即用型中继库

https://github.com/JakeWharton/RxRelay

有很好的文档,但是从本质上讲,Relay是一个Subject,除了不能调用onComplete或onError。

,选项为:

行为继电器

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

答案 10 :(得分:0)

您可以使用 onErrorComplete() 方法跳过错误

mSubscription = reactiveLocationProvider.getUpdatedLocation(mLocationRequest)
    .buffer(50)
    .flatMapMaybe(locations -> Maybe.just(mRestService.postLocations(locations).onErrorComplete()) // skip item
    .subscribeOn(Schedulers.newThread())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe();