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"));
Related
In the vertx route handler below, is it ok to send the response inside the executeBlocking handler?
router.route(HttpMethod.POST, "/some-path").handler(context -> {
vertx.executeBlocking(completePromise -> {
// Do some time-consuming work
context.response().end("the response");
});
Or do I have to do it like below, in the original event loop?
router.route(HttpMethod.POST, "/some-path").handler(context -> {
vertx.executeBlocking(completePromise -> {
// Do some time-consuming work
completePromise.complete();
}, resultHandler -> {
if (resultHandler.succeeded()) {
context.response().setStatusCode(400).end("failed");
} else {
context.response().end("the response");
}
});
});
Both are correct, although it is a better practice to interact with Vert.x API elements in the original event loop. (for safety and performance).
I'm learning android development I'm using RXJava with retrofit to interact with an API. So i've successfully managed to GET Data from my API. The problem now is that the program continues before the data has been fetched. How can I wait for the data to be downloaded from the API and then run some function?
I have a retrofit client as shown
public class RetrofitClient {
private static Retrofit retrofit = null;
private RetrofitClient() {
}
public static Retrofit getClient(String baseUrl) {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(SimpleXmlConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}
return retrofit;
}
}
I do an API call as such.
public void getData(MyFragment Fragment) {
mAPIService.getData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Data>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Log.i("ERROR IN GET DATA", e.toString());
}
#Override
public void onNext(Data response) {
Log.i("MY DATA", response.toString());
fragment.downloadData(response);
}
});
}
the problem is my android application does not wait for fragment.downloadData(response) to finish but instead continues executing code and then crashes because response is null.
I have a listener on a button that when clicked gets data from the API
button.setOnClickListener(v ->{
APICaller.getData(this);
Log.i("TEST", data.ToString()); //THIS IS NULL
});
This is the downloadData function that I run from the APICaller
public void downloadData(Data data) {
this.data = data;
}
You need to be waiting for your RxJava stream to emit a value (either error or response).
Firstly, for this, if you're expecting a "single" emission, success or failure, I would use a Single. At the moment it looks your mAPIService.getData() method is returning an Observable. These are meant for streams that are going to emit multiple values which in your cause I am assuming is not what is going to happen. You only have one item that is going to be emitted so I would look at returning a Single. Not part of your question but FYI.
What I like to do is to tell my UI that whatever I'm doing is "loading", normally in the doOnSubscribe. Then the UI knows to show a loading icon or to not allow user interactions or something. Something like this (nb. notice how its after the observeOn(AndroidSchedulers.mainThread). Any time you interact with UI elements, do it on the main thread. I think this will do it):
mAPIService.getData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(new Consumer<Disposable>() {
#Override
public void accept(Disposable disposable) throws Exception {
fragment.loading();
}
})
Then when either the onError or onSuccess returns in your subscription, that is where you tell the UI it's ready to proceed. You then either have a valid response or can show the error to the user.
EDIT: Reactive Programming
After your comments it looks like you do not understand reactive programming. It has a bit of a steep learning curve and I still struggle with it today.
Your APICaller class, whatever it is, should return the Observable itself. You shouldn't be passing in a Fragment to this and handling it within there as you're opening yourself up to memory leaks and its a bit of a code smell. A better option is to just return the Observable returned by mAPIService.getData(). That's it. At the moment you are pushing the Observable to another thread using Schedulers.io() which says to your main thread, carry on and don't wait for me. You then come back to this thread when a response is emitted using the code .observeOn(AndroidSchedulers.mainThread())
In your fragment is then where you handle the emission of a value or an error. Your fragment code then becomes:
button.setOnClickListener(v ->{
APICaller.getData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Data>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Log.i("ERROR IN GET DATA", e.toString());
}
#Override
public void onNext(Data response) {
Log.i("MY DATA", response.toString());
this.data = response
Log.i("TEST: "+data.toString());
}
});
});
I would like to recommend you watch some tutorials, do some reading and I'd also like to refer you back to my point about Singles at the start
I have an HTTP request that triggers a long-running task (multiple HTTP requests to another service) that is supposed to be completed in the background while the original requests complete.
So what I do is
public void triggerWork(#RequestBody SomeObject somObject) {
return new ResponseEntity<>(startWorkAndReturn(somObject), HttpStatus.OK);
}
public void startWorkAndReturn(SomeObject someObject) {
Observable.create(observableEmitter -> {
// do the work with someObject here and at some time call
observableEmitter.onNext("result");
}).subscribe(new Observer<Object>() {
#Override
public void onSubscribe(Disposable disposable) {
}
#Override
public void onNext(Object o) {
// called at some unknown time
}
#Override
public void onError(Throwable throwable) {
}
#Override
public void onComplete() {
// currently not used as all the work is done in onNext but maybe that's a mistake
}
});
return;
}
But this seems to block the request until all the work has been done. Which already seems odd to me, since I never call onComplete, which in itself might be a mistake. But still, I am wondering how to create a request that immediately returns after triggering a background worker.
Is Flowables the solution here? I am going to refactor to those anyways to handle backpressure. Or do I need to create a background worker Thread? What is the best practice here?
Thanks
I would use Observable.fromCallable{} since you need emit only single event. That will handle onCompleate call. From information you share I don`t know how can you properly handle disposable. You should add subscribeOn() and observeOn() operators that will define on which thread 'work' should be processed and result should be observed.
Docs ref:
http://reactivex.io/RxJava/javadoc/io/reactivex/Observable.html#fromCallable-java.util.concurrent.Callable-
http://reactivex.io/documentation/operators/subscribeon.html
http://reactivex.io/documentation/operators/observeon.html
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();
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.