Я хотел бы знать, как игнорировать исключения и продолжать бесконечный поток (в моем случае поток местоположений)?
Я получаю текущую позицию пользователя (используя 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);
}
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();
Этот ответ может быть немного запоздалым, но если кто-то наткнется на это, вместо того, чтобы изобретать колесо, можно использовать готовую к использованию библиотеку 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);
С помощью Rxjava2 мы могли бы вызвать перегруженную плоскую карту с параметром delayErrors: плоская карта javadoc
При передаче как истины:
исключения из текущего Flowable и всех внутренних издателей задерживаются до тех пор, пока все они не завершатся, если false, первый сигнализирующий об исключении немедленно завершит всю последовательность
Вот моя функция расширения kotlin для игнорирования ошибок
fun <T> Observable<T>.ignoreErrors(errorHandler: (Throwable) -> Unit) =
retryWhen { errors ->
errors
.doOnNext { errorHandler(it) }
.map { 0 }
}
Это использует retryWhen
для повторной подписки на восходящий поток на неопределенный срок, в то же время позволяя вам использовать метод обработки ошибки нетерминальным способом.
Это опасно
Небольшая модификация решения (@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();
}
};
}
}
Добавьте мое решение этой проблемы:
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();
}
};
Попробуйте вызвать остальную службу в вызове 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>, но я думаю, что и в вашем случае это сработает.
Просто вставьте информацию о ссылке из ответа @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 в цепочке после того, как он выдает исключение, все еще вероятно, что источник будет отписан.
Если вы просто хотите игнорировать ошибку внутри flatMap
без возврата элемента , сделайте следующее:
flatMap(item ->
restService.getSomething(item).onErrorResumeNext(Observable.empty())
);
Вы можете использовать один из операторов обработки ошибок.
onErrorResumeNext( )
- инструктирует Observable генерировать последовательность элементов, если он обнаруживает ошибкуonErrorReturn( )
- указывает Observable выдавать определенный элемент при обнаружении ошибки.onExceptionResumeNext( )
- инструктирует Observable продолжать испускать элементы после того, как он встречает исключение (но не другой вариант throwable)retry( )
- если источник Observable выдает ошибку, повторно подпишитесь на него в надежде, что он завершится без ошибокretryWhen( )
- если источник Observable выдает ошибку, передать эту ошибку другому Observable, чтобы определить, следует ли повторно подписаться на источник
Особенно многообещающе выглядят retry
и onExceptionResumeNext
в вашем случае.
Похожие вопросы
Связанные вопросы
Новые вопросы
android
Android — это мобильная операционная система Google, используемая для программирования или разработки цифровых устройств (смартфонов, планшетов, автомобилей, телевизоров, одежды, очков, IoT). Для тем, связанных с Android, используйте теги, специфичные для Android, такие как android-intent, android-activity, android-adapter и т. д. Для вопросов, отличных от разработки или программирования, но связанных с Android framework, используйте эту ссылку: https://android .stackexchange.com.