RxJava + Retrofit + polling - java

I have a Retrofit call and want to recall it every 30sec. To do that I use an Observable.interval(0, 30, TimeUnit.SECONDS)
Observable
.interval(0, 30, TimeUnit.SECONDS)
.flatMap(x -> RestApi.instance().getUsers())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(list -> {
// ...
},
error -> Timber.e(error, "can't load users"));
My problem: If the api call fails, onError is called and the subscription unsubscribes and the polling isn't working anymore :-(
To catch the api error I added a retryWhen
Observable
.interval(0, 30, TimeUnit.SECONDS)
.flatMap(x -> RestApi.instance().getUsers()
.retryWhen(errors -> errors
.flatMap(error -> Observable.timer(15, TimeUnit.SECONDS))))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(list -> {
// ...
},
error -> Timber.e(error, "can't load users"));
This catches the error but I get multiple api calls over the time. Every 30sec I get a new poll signal which ends in a new api request. But if the api request fails it retries itself. So I have a new request plus all retries.
My question: How can I handle an api error without unsubscribing from the poll signal?

Read how to properly use retryWhen and repeatWhen.
http://blog.danlew.net/2016/01/25/rxjavas-repeatwhen-and-retrywhen-explained/
And how to use onError operators:
http://blog.danlew.net/2015/12/08/error-handling-in-rxjava/
It's really easy w Rx :) I'm not gonna give you a final solution, just play around with it and try to understand the flow here.

If you want your getUsers request to not end up in onError when a request fails, instead of returning Observable<yourUserType> getUsers(), make it return an Observable<Response<yourUserType>> getUsers(). This way you'll be able to intercept the network error in the Response object.
This method works only if you are using retrofit 2.x

You can use onErrorResumeNext or onExceptionResumeNext and pass there "error" value.
You can look for other error handlings depends on your needs here

You can use this code, in this code implement the numbers attempts and time delay between request
private static int COUNTER_START = 1;
private static final int ATTEMPTS = 6;
private static final int ORIGINAL_DELAY_IN_SECONDS = 2;
remoteData.getAllRides(idSearch)
.repeatWhen(new Func1<Observable<? extends Void>, Observable<?>>() {
#Override
public Observable<?> call(Observable<? extends Void> observable) {
return observable.flatMap(new Func1<Void, Observable<?>>() {
#Override
public Observable<?> call(Void aVoid) {
if(COUNTER_START > ATTEMPTS){
throw new RuntimeException();
}
COUNTER_START++;
return Observable.timer(ORIGINAL_DELAY_IN_SECONDS, TimeUnit.SECONDS);
}
});
}
})
.takeUntil(new Func1<RideResponse, Boolean>() {
#Override
public Boolean call(RideResponse rideResponse) {
return rideResponse.getState().equals("finished");//this is the validation finish polling
}
}).filter(new Func1<RideResponse, Boolean>() {
#Override
public Boolean call(RideResponse rideResponse) {
return rideResponse.getState().equals("finished"); //this is the validation finish polling
}
}).map(rideResponse -> Log.e("",rideResponse.toString()))
.doOnError(err -> Log.e("Polling", "Error retrieving messages: " + err));

Related

Transpose from Consumer to CompletableFuture

I'm currently using an API which I unfortunately cannot change easily. This API has some methods in the style of this:
public void getOffers(Consumer<List<Offer>> offersConsumer) {
final Call<List<Offer>> offers = auctionService.getOffers();
handleGetOffers(offersConsumer, offers);
}
It's a web api using retrofit, and it enables me to process the response in a consumer, but I much rather want to work with CompletableFutures.
I'm using the data I receive from this endpoint to compose an interface in a game, and therefore compose an inventory, that basically acts as a frontend to the api. What I want to do, is to have my composing method to wait for the consumer to finish, and then provide the processed results. This is what I have so far, but I don't know how to do the step from the consumer to the CompletableFuture:
#Override
public CompletableFuture<Inventory> get(Player player) {
return CompletableFuture.supplyAsync(() -> {
auctionAPI.getOffers(offers -> {
//process the offers, then return the result of the processing, in form of an "Inventory"-Object.
}
});
});
}
I now need to return the result of the processing after all the Items have been received and then processed. How can I achieve this?
Something along the lines should work:
#Override
public CompletableFuture<Inventory> get(Player player) {
CompletableFuture<Inventory> result = new CompletableFuture<>();
CompletableFuture.supplyAsync(() -> {
auctionAPI.getOffers(offers -> {
//process the offers, then return the result of the processing, in form of an "Inventory"-Object.
result.complete(inventory);
}
});
return null;
});
return result;
}

RxJava 2 - how to cancel infinite stream upon error and handle it?

I've got the following infinite stream which does something every second.
What I want is to stop the stream upon error and handle it.
How can I achieve that?
void doSomething() {
Disposable disposable = execute(doSomethingInner(), 0L, TimeUnit.SECONDS, schedulerProvider.io(), someClass -> 1L).doOnError
(throwable -> {
Timber.e(throwable, "error happened");// Never triggered
})
.doOnNext(someClass -> Timber.i("doing the infinite stuff"))
.subscribe(Functions.emptyConsumer(), throwable -> {
Timber.e(throwable, "stop doing the infinite stuff");// Never triggered
});
}
Observable<SomeClass> doSomethingInner() {
return Observable.error(new Exception("something went wrong"));
}
Observable<SomeClass> execute(Observable<SomeClass> source,
long delayInterval,
TimeUnit timeUnit,
Scheduler scheduler,
Function<SomeClass, Long> interval) {
return Observable.defer(new Callable<ObservableSource<SomeClass>>() {
long currentInterval = delayInterval;
#Override
public ObservableSource<SomeClass> call() {
return Single.timer(currentInterval, timeUnit, scheduler)
.flatMapObservable(o -> source)
.doOnNext(t -> currentInterval = interval.apply(t));
}
})
.repeat()
.retry();
}
I think retry() is consuming your error.
Try to either:
Remove this retry() completely
or change it to retry(Predicate<Throwable>) to decide whether to repeat.
It is default behavior of subscriber to cancel stream on error if you don't consume it earlier, and you should receive callback to onError() inside subscribe().

RxJava: How can I wrap a multi-step operation, returning the step complete to observers?

Lets say I have a login and user data method, representing HTTP calls:
Single<LoginResponse> login();
Single<UserData> userData();
I need to call login() then userData(). If both succeed, the user is logged in.
I know how to wrap them up in a e.g. Completable:
Completable performLogin() {
login().doOnSuccess(this::storeLoginResponse)
.flatMap(userData())
.doOnSuccess(this::storeUserData)
.doOnError(this::wipeLoginData)
.toCompletable();
}
So the UI then says
showLoading();
performLogin().subscribe(() -> {
stopLoading();
onLoginSuccess();
}, error -> {
stopLoading();
onLoginFailure();
});
What if the UI needs to show which stage of the loading is happening? As in, when the login() call completes and the userData() call starts it will change the UI?
What I thought of is something like
Observable<LoginStage> performLogin() {
return Observable.create(emitter -> {
login.doOnSuccess(response -> {
storeLoginResponse(response)
emitter.onNext(LOGIN_COMPLETE)
}).flatMap(userData())
.subscribe(userData -> {
storeUserData(userData);
emitter.onNext(USER_DATA_COMPLETE)
emitter.onComplete();
}, error -> {
wipeLoginData();
emitter.onError(error);
});
});
}
But it feels like there's a nicer or more Rx-y way to do it.
You can use hot observables and chain one observable to another Subject and pass all items form one emission to another if you need it.
#Test
public void chainObservablesWithSubject() throws InterruptedException {
Observable<Long> observable = Observable.from(Arrays.asList(1l, 2l, 3l, 4l));
Subject<Long, Long> chainObservable = ReplaySubject.create(1);
observable.subscribe(chainObservable);
chainObservable.subscribe(System.out::println, (e) -> System.err.println(e.getMessage()), System.out::println);
}
You can check more examples here https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/connectable/HotObservable.java

How to call a non-rx network call that depends on an rx network call

I have a network call that returns an Observable, and I have another network call that it is not rx that depends on the first Observable and I need to somehow convert it all with Rx.
Observable<Response> responseObservable = apiclient.executeRequest(request);
After executing I need to do another http call that does not return an Observable:
responseObservable.map(response - > execute the no rx network call using the response.id)
noRxClient.getInformation(response.id, new Action1<Information>() {
#Override
public void call(Information information) {
//Need to return information with page response
}
});
After then I need to call this method to render the response
renderResponse(response, information);
How can I connect the non-rx call with the rx and then call render response all with RxJava?
You can wrap your async non-rx calls into Observable using Observable.fromEmitter (RxJava1) or Observable.create (RxJava2) and Observable.fromCallable (for non-async calls):
private Observable<Information> wrapGetInformation(String responseId) {
return Observable.create(emitter -> {
noRxClient.getInformation(responseId, new Action1<Information>() {
#Override
public void call(Information information) {
emitter.onNext(information);
emitter.onComplete();
//also wrap exceptions into emitter.onError(Throwable)
}
});
});
}
private Observalbe<RenderedResponse> wrapRenderResponse(Response response, Information information) {
return Observable.fromCallable(() -> {
return renderResponse(response, information);
//exceptions automatically wrapped
});
}
And combine results using overloaded flatMap operator:
apiclient.executeRequest(request)
.flatMap(response -> wrapGetInformation(response.id),
(response, information) -> wrapRenderResponse(response, information))
)
//apply Schedulers
.subscribe(...)

RxJava: How to get all results AND errors from an Observable

I'm working on a project that involves Hystrix, and I decided to use RxJava. Now, forget Hystrix for the rest of this because I believe the main problem is with my complete screwing up of writing the Observable code correctly.
Need:
I need a way to return an observable that represents a number of observables, each running a user task. I want that Observable to be able to return all results from the tasks, even errors.
Problem:
Observable streams die on errors. If I have three tasks and the second task throws an exception, I never receive the third task even if it would have succeeded.
My Code:
public <T> Observable<T> observeManagedAsync(String groupName,List<EspTask<T>> tasks) {
return Observable
.from(tasks)
.flatMap(task -> {
try {
return new MyCommand(task.getTaskId(),groupName,task).toObservable().subscribeOn(this.schedulerFactory.get(groupName));
} catch(Exception ex) {
return Observable.error(ex);
}
});
}
Given that MyCommand is a class that extends HystrixObservableCommand, it returns an Observable and so shouldn't figure in on the problems I'm seeing.
Attempt 1:
Used Observable.flatMap as above
Good: Each Command is scheduled on it's own thread and the tasks run asynchronously.
Bad: On first Command exception, Observable completes having emitted previous successful results and emitting the Exception. Any in-flight Commands are ignored.
Attempt 2:
Used Observable.concatMapDelayError instead of flatMap
Bad: For some reason, tasks run synchronously. Why??
Good: I get all the successful results.
~Good: OnError gets a Composite exception with a list of the exceptions thrown.
Any help will be greatly appreciated and probably result in me being very embarrassed for not having thought of it myself.
Additional Code
This test succeeds with Observable.flatMap, but fails when using Observable.concatMapDelayError because the tasks do not run asynchronously:
java.lang.AssertionError: Execution time ran over the 350ms limit: 608
#Test
public void shouldRunManagedAsyncTasksConcurrently() throws Exception {
Observable<String> testObserver = executor.observeManagedAsync("asyncThreadPool",getTimedTasks());
TestSubscriber<String> testSubscriber = new TestSubscriber<>();
long startTime = System.currentTimeMillis();
testObserver.doOnError(throwable -> {
System.out.println("error: " + throwable.getMessage());
}).subscribe(testSubscriber);
System.out.println("Test execution time: "+(System.currentTimeMillis()-startTime));
testSubscriber.awaitTerminalEvent();
long execTime = (System.currentTimeMillis()-startTime);
System.out.println("Test execution time: "+execTime);
testSubscriber.assertCompleted();
System.out.println("Errors: "+testSubscriber.getOnErrorEvents());
System.out.println("Results: "+testSubscriber.getOnNextEvents());
testSubscriber.assertNoErrors();
assertTrue("Execution time ran under the 300ms limit: "+execTime,execTime>=300);
assertTrue("Execution time ran over the 350ms limit: "+execTime,execTime<=350);
testSubscriber.assertValueCount(3);
assertThat(testSubscriber.getOnNextEvents(),containsInAnyOrder("hello","wait","world"));
verify(this.mockSchedulerFactory, times(3)).get("asyncThreadPool");
}
Tasks for the above unit test:
protected List<EspTask<String>> getTimedTasks() {
EspTask longTask = new EspTask("helloTask") {
#Override
public Object doCall() throws Exception {
Thread.currentThread().sleep(100);
return "hello";
}
};
EspTask longerTask = new EspTask("waitTask") {
#Override
public Object doCall() throws Exception {
Thread.currentThread().sleep(150);
return "wait";
}
};
EspTask longestTask = new EspTask("worldTask") {
#Override
public Object doCall() throws Exception {
Thread.currentThread().sleep(300);
return "world";
}
};
return Arrays.asList(longTask, longerTask, longestTask);
}
You can use Observable.onErrorReturn(), and return special value (e.g. null), then filter non-special values downstream. Keep in mind that source observable will complete on error. Also depending on use case Observable.onErrorResumeNext()methods can be useful aswell. If you are interested in error notifications, use Observable.materialize(), this will convert items and onError(), onComplete() into Notifications, which then can be filtered by Notification.getKind()
Edit.
All operators mentioned above should be added right after .toObservable().subscribeOn(this.schedulerFactory.get(groupName)); assuming try/catch was absent.
You want to use mergeDelayError:
public <T> Observable<T> observeManagedAsync(String groupName,List<EspTask<T>> tasks) {
return Observable.mergeDelayError(Observable
.from(tasks)
.map(task -> {
try {
return new MyCommand(task.getTaskId(),groupName,task).toObservable().subscribeOn(this.schedulerFactory.get(groupName));
} catch(Exception ex) {
return Observable.error(ex);
}
}));
}
Note that your MyCommand constructor should not throw any exceptions; this allows your code to be written more concisely:
public <T> Observable<T> observeManagedAsync(String groupName,List<EspTask<T>> tasks) {
return from(tasks)
.map(task -> new MyCommand(task.getTaskId(), groupName, task)
.toObservable()
.subscribeOn(this.schedulerFactory.get(groupName)))
.compose(Observable::mergeDelayError);
}
Keep in mind that this will still invoke onError at most once; if you need explicit handling of all errors, use something like an Either<CommandResult, Throwable> as the return type (or handle the errors and return an empty observable).
Use .materialize() to allow all emissions and errors to come through as wrapped notifications then deal with them as you wish:
.flatMap(task -> {
try {
return new MyCommand(task.getTaskId(),groupName,task)
.toObservable()
.subscribeOn(this.schedulerFactory.get(groupName))
.materialize();
} catch(Exception ex) {
return Observable.error(ex).materialize();
}
});

Categories