stopping a long poll with rxjava in android - java

I am doing a long poll to an API from an android client using retrofit and rxjava. In this case, we wait for a 200 or 408 timeout response from an API and handle the response or reconnect to wait again for more data. This works just fine. I need to stop rx from retrying on certain error codes (like a 500) or if I want to interrupt the process, for example my app was background so let's stop the long poll.
retrofitInterface.startPolling() //returns an Observable
.repeat()
.retry()
.subscribe(new Subscriber<List<Stuff>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Stuff> updates) {
//process stuff
}
}
});
I'm not sure if repeatWhen and retryWhen is the right solution here, where I want to keep repeating and retrying http calls to the API but stop repeating in some condition (say I flip a bool in the class to false) or stop retrying if the status code is a 500 instead of say a 408.

It's easier if you wrap your request answer in object of type <Response<?>>, this gives you control over the error code.
What I did for that use case is throwing a specific exception when I have some specific error code:
public <T> T throwExceptionIfFailure(T res) {
Response result = (Response<?>) res;
if (!result.isSuccessful()) {
try {
String msg = result.errorBody().string();
if (result.code() == 401 || result.code() == 403) {
invalidateToken();
msg = context.getString(R.string.invalid_credential);
} else if (result.code() == 502) {
msg = context.getString(R.string.server_down);
}
throw Exceptions.propagate(new IOException(msg));
} catch (Throwable e) {
throw Exceptions.propagate(e);
}
} else {
return res;
}
}
and I added this method in a map function of RX:
serviceRetrofit.getContacts()
.map(result -> serviceRetrofit.throwExceptionIfFailure(result))
.map(result -> createOrUpdateContact(result))
.retry(4)
.onErrorReturn(error -> handleErrorEvent(error))
.doOnCompleted(() -> emitStoreChange(new Store.StoreChangeEvent()))
.subscribe();

Related

Ways to cancel downstream tasks in CompletableFuture chain when exception fired?

My database methods return a CompletableFuture to do the work with database asynchronously and then process the result on the main thread.
public interface IAccountDAO {
CompletableFuture<ObjectId> create(AccountEntity accountEntity);
CompletableFuture<Optional<AccountEntity>> get(String nickname);
CompletableFuture<Void> update(AccountEntity accountEntity);
CompletableFuture<Void> delete(AccountEntity accountEntity);
}
Each of the methods have following code inside (example of get(String) method):
#Override
public CompletableFuture<Optional<AccountEntity>> get(String nickname) {
return CompletableFuture.supplyAsync(() -> {
try {
// something logic
return Optional.of(...);
} catch (SomethingException ex) {
ex.printStackTrace();
throw new CompletionException(ex);
}
});
}
Handling the result:
CompletableFuture<Optional<AccountEntity>> cf = get("Test_Nickname");
// Notify end user about exception during process
cf.exceptionally(ex -> {
System.out.println("Database operation failed. Stacktrace:");
ex.printStackTrace();
return Optional.ofEmpty(); // I must return something fallback value that passes to downstream tasks.
});
// Downstream tasks that I would to cancel if exception fired
cf.thenAccept(...);
cf.thenRun(...);
cf.thenRun(...);
So, operation with database can fire exception. In this case I would to pass exception and using .exceptionally(...) or something like this notify the user called that method and STOP chain executing (cancel downstream tasks).
My question is: How I can cancel downstream tasks when CompletableFuture completed with exception?
I don't think you can cancel the downstream tasks.
All you can do is just not execute the downstream tasks incase of exception.
CompletableFuture<Optional<AccountEntity>> cf = get("Test_Nickname");
cf.whenComplete((response, exception) -> {
if (exception == null) {
// Downstream tasks that I would to like to run on this response
} else {
//Notify end user of the exception
}
});
To avoid nesting.
private Response transformResponse(GetResponse r) {
try {
return SuccessResponse();
} catch (Exception e) {
return FailedResponse();
}
}
get("Test_Nickname")
.thenApply(r -> transformResponse(r))
.thenCompose(... //update)

How to call api with delay until response is ok

Need some code recommendations. I need to call api which run some proccess on service.
First response show that proccess is begin and need to check each minute is proccess completed calling api. Now I just call Thread.Sleep before each request, this way quick but dirty. how can i improve the code?
// call api, that run some process. it`s long operation which can take up to 15 minutes
boolean isDone = false;
Response<?> proccessRunResponse = execute(request());
// if sucess, operation started successfully
if(proccessRunResponse.isSuccess()) {
// now i need to check status each minute and then retry operation(run proccess req, check status) if the process is complete
while (!isDone) {
waitUntilIsDone()
// request for check status that need to call until response is comleted
Response<?> status = execute(getRequestForCheckStatus());
ResponceWrapper res = (ResponceWrapper) status;
// status can be: in progess, queued etc..
if (res.isComplete()) {
isDone = true;
// exit loop and retry operation
}
}
}
}
private void waitUntilTaskDone() throws InterruptedException {
TimeUnit.SECONDS.sleep(60);
}
In your scenario, you can use java.util.concurrent.CompletableFuture of jdk to finish. The below is pseudocode to show how to do. You should modify it according to your actual code.
import java.util.concurrent.CompletableFuture;
// invoke request() in asynchronous
CompletableFuture<Response> cf = CompletableFuture.supplyAsync(() -> {
return execute(request());
});
// if success:
cf.thenAccept((result) -> {
System.out.println("response" + result);
});
// if fail
cf.exceptionally((e) -> {
e.printStackTrace();
return null;
});
// here should do other things

RxJava capture network response code when there is no reponse body

Trying to get up to speed with RxJava. I have a network call that returns no data. The only response is the code (happy path: 200, 4xx otherwise). I want to listen for this response, but all I can find is how to do it with some sort of response object.
#GET
Observable<Response<ResponseBody>> makeHttpCall(#Url String url);
So my RxJava code looks like this:
myRetrofit.makeHttpCall(url)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Response<ResponseBody>>() {
#Override
public void onCompleted() {
Timber.d("on completed");
}
#Override
public void onError(Throwable e) {
if (!(e instanceof EOFException)) {
Timber.e(e, "error occurred");
}
}
#Override
public void onNext(Response<ResponseBody> responseBodyResponse) {
Timber.d("on next");
}
});
This works, but it seems like the wrong solution. I don't like how my observer drops into the onError method. My response is a 200, so I'd like to see it in the onNext or onCompleted methods.
I looked into using Completable, but that didn't work at all. I still think that might be the right way to go, however.
What is the best approach here? I'm wondering if the issue simply traces to my use of <Response<ResponseBody>> and whether there is a different type that is more appropriate in this case.
If you only care about the Http code response then something like this should surfice :
Api:
#GET
Single<Response<ResponseBody>> makeHttpCall(#Url String url);
Call:
myRetrofit.makeHttpCall(url)
.subscribeOn(Schedulers.io())
.map(Response::code)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
i -> Timber.d("code " + i),
e -> {
if (!(e instanceof EOFException)) {
Timber.e(e, "error occurred");
}
});
Also note in your original code you pass the Response<ResponseBody> to the Observer on the main thread - interacting with the ResponseBody on this thread will cause a NetworkOnMainThreadException as dealing with the body is considered a IO operation - I know not your desired intention here, but worth noting when you make api calls that require interaction with the body.
Documentation:
http://reactivex.io/RxJava/javadoc/rx/Observable.html#subscribe(rx.functions.Action1,%20rx.functions.Action1,%20rx.functions.Action0)
Subscription subscribe(Action1 onNext,
Action1 onError, Action0 onCompleted)
Subscribes to an Observable and provides callbacks to handle the items it emits and any error or completion notification it issues.
myRetrofit.makeHttpCall(url)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(n->Timber.d("on next"), e->{
if (!(e instanceof EOFException)) {
Timber.e(e, "error occurred");
}
}, ()->Timber.d("on completed"));

Java - AsyncHttpClient - aborting ongoing asynchronous call

I am using HttpAsyncClient in order to GET some urls. In some cases, let's say when I receive status HTTP-304, I would like to abandon the ongoing call. I don't want to wait for body, I don't want to spend the machine resources on it. Is there any way of cancelling this? Apparently things like futureResponse.cancel(true) or futureResponse.finalize() do not work.
Future<Response> futureResponse = httpClient.prepareGet("some-url").addHeader("some", "header").execute(new AsyncCompletionHandler<Response>() {
#Override
public State onStatusReceived(HttpResponseStatus status) throws Exception {
logger.info("status {}", status);
// CONDITIONAL FINISH THIS CALL
return super.onStatusReceived(status);
}
#Override
public Response onCompleted(Response response) throws Exception {
logger.info("its ok");
return response;
}
#Override
public void onThrowable(Throwable t) {
logger.error(t);
}
});
From the doc:
You can return ABORT to close the connection.
#Override
public State onStatusReceived(HttpResponseStatus responseStatus) throws Exception {
Integer status = responseStatus.getStatusCode();
if (304 == status) { // CONDITIONAL FINISH THIS CALL
return State.ABORT;
} else {
doSomething()
return ...
}
}

How to make RxJava interval to perform action instantly

Hello I am making observable to ask my server about its online/offline status every 15 seconds:
public Observable<Response> repeatCheckServerStatus(int intervalSec, final String path) {
return Observable.interval(intervalSec, TimeUnit.SECONDS)
.flatMap(new Func1<Long, Observable<Response>>() {
#Override
public Observable<Response> call(Long aLong) {
return Observable.create(new Observable.OnSubscribe<Response>() {
#Override
public void call(Subscriber<? super Response> subscriber) {
try {
Response response = client.newCall(new Request.Builder()
.url(path + API_ACTION_CHECK_ONLINE_STATUS)
.header("Content-Type", "application/x-www-form-urlencoded")
.get()
.build()).execute();
subscriber.onNext(response);
subscriber.onCompleted();
if (!response.isSuccessful())
subscriber.onError(new Exception());
} catch (Exception e) {
subscriber.onError(e);
}
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
});
}
After I call this method, first execution of code will be after intervalSec time (15sec in my case). Looking at rxJava docummentation of interval method:
http://reactivex.io/documentation/operators/interval.html
This is how it should be.
Question: is there any way to execute code instantly and then repeat in intervals?
You can execute it immediately also like this:
Observable.interval(0, 1000, TimeUnit.MILLISECONDS).subscribe();
What you are looking for is startWith
Observable.interval(15, SECONDS).startWith(1);
This will get the updates from the interval, but emit one item immediately after subscribing.
you can use `
Observable.interval(1, TimeUnit.SECONDS).startWith(0)
`
It is duplicate value "0" in subscribe.

Categories

Resources