I'm trying to run 2 observables sequentially although, if the first one gives a specific error I would like to stop the concat and execute another thing.
Despite, the first observable might giving many exceptions, I just want to stop propagating the concat when a specific error is reached (in this case HttpException 403), on the other hand I want to proceed with the concat execution when there isn't any 403 HttpException.
I've done the following code, but so far I cannot proceed the concat execution with replacing the current observable to Observable.error(throwable).
Is there a method to do it without changing the observable logic or the subscriber logic?
Or something to stop propagating a concat on a specific error, and continue if other error is given?
Observable.concat(getObservable1(), getObservable2())
.onErrorResumeNext(new Func1<Throwable, Observable<? extends OperationModel>>() {
#Override
public Observable<? extends OperationModel> call(Throwable throwable) {
if(throwable instanceof HttpException && ((HttpException)throwable).code() == 403) {
return Observable.error(throwable);
}
return Observable.empty(); //here I just want to proceed the concat despite giving an error
}
.subscribeOn(Schedulers.io()) //execute requests should be on io() thread
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomRequestSubscriber<>());
Try this:
Observable.concat(getObservable1().onErrorResumeNext(new Func1<Throwable, Observable<? extends OperationModel>>() {
#Override
public Observable<? extends OperationModel> call(Throwable throwable) {
if(throwable instanceof HttpException && ((HttpException)throwable).code() == 403) {
return Observable.error(throwable);
}
return Observable.empty(); //here I just want to proceed the concat despite giving an error
}), getObservable2())
.subscribeOn(Schedulers.io()) //execute requests should be on io() thread
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomRequestSubscriber<>());
This should catch the error in the first observable, and then you can continue with the second observable.
Related
I have two async methods, that got to be called while one operation. Each method could be completed successfully or retrieve with error. On case of error, I got to retry call each method once again, with delayed of 2 sec. Mean, I should call both methods, despite of result of one of them. In error callback I want to know in which method error occured, or in both methods.
It seems I should use Completable for this, but I'm absolutely newbie in Rx.
private void method1(final CompletableEmitter e, String path){
Database.getInstance().getReference(path).addListener(new Listener() {
#Override
public void onDataChange(Data data) {
//todo something
e.onComplete();
}
#Override
public void onCancelled(DatabaseError databaseError) {
e.onError(new Throwable(databaseError.getMessage()));
}
});
}
Method2 is the same.
The following code doesn't work properly.
Completable completable1 = Completable.create(method1(e););
Completable completable2 = Completable.create(method2(e););
completable1
.doOnError(…)
.retry(1)
.andThen(completable2 //never called if completable1 gets onError each time
.retry(1)
.doOnError(…))
.subscribe(…).dispose();
You have a lot of ways to do this. I'm going just to limit to explain how to achieve this using two Completables
Let's say you have two completables:
Completable doSomething = ...
Completable doSomethingElse = ...
To execute these sequentially,
you can concatenate them using andThen operator. Then to delay a retry when an error occurs, you can use retryWhen:
doSomething.andThen(doSomethingElse)
.retryWhen { Flowable.timer(2, TimeUnit.SECONDS) }
.subscribe()
This snippet above will retry infinitely if an error is permanently occurring. To go beyond, you can limit the number of tries using:
.retryWhen { errors ->
val retryCounter = AtomicInteger()
errors.flatMap {
if (retryCounter.getAndIncrement() <= 3)
Flowable.timer(2, TimeUnit.SECONDS)
else Flowable.error(it)
}
}
If you want to retry only when a given type of error occurs, you can use:
.retryWhen { errors ->
val retryCounter = AtomicInteger()
errors.flatMap {
if (it is YourSpecficError && retryCounter.getAndIncrement() <= 3)
Flowable.timer(2, TimeUnit.SECONDS)
else Flowable.error(it)
}
}
In the case you want to retry each one independently, you can use:
doSomething.retryWhen { ... }
.andThen(doSomethingElse.retryWhen { ... })
.subscribe()
In addition, in order to avoid the retryWhen logic duplication, you could encapsulate this in an extension function:
fun Completable.retryDelayed(): Completable {
return this.retryWhen { errors ->
val retryCounter = AtomicInteger()
errors.flatMap {
if (it is YourSpecficError && retryCounter.getAndIncrement() <= 3)
Flowable.timer(2, TimeUnit.SECONDS)
else Flowable.error(it)
}
}
}
If you want to run your completables in parallel you ca use merge operator:
Completable doAll = Completable.merge(listOf(doSomething, doSomething))
I am learning how to do data polling in RxJava2
Here is my code so far.
private io.reactivex.Single<String> getMyTask() {
return io.reactivex.Single.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
Log.d("ERSEN","Task Started!");
Random random = new Random(System.currentTimeMillis());
if(random.nextBoolean()){
return "WORK COMPLETED";
}
Log.d("ERSEN","Task Had An Error!");
throw new IllegalArgumentException();
}
});
}
The above is my Single which emits a String basically simulating some work.
I also make the task randomly succeed and fail to the test the case when a poll event fails to check if re-subscription occurs correctly
My problem
compositeDisposable.add(getMyTask()
.repeatWhen(new Function<Flowable<Object>, Publisher<?>>() {
#Override
public Publisher<?> apply(final Flowable<Object> objectFlowable) throws Exception {
return objectFlowable.delay(INTERVAL, TimeUnit.SECONDS);
}
})
.retryWhen(throwableFlowable -> throwableFlowable.flatMap(new Function<Throwable, Publisher<?>>() {
#Override
public Publisher<?> apply(Throwable throwable) throws Exception {
if (throwable instanceof ClassCastException) {
return Flowable.error(throwable);
}
return throwableFlowable.delay(INTERVAL, TimeUnit.SECONDS);
}
}))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onSuccess, this::onError));
In the above, I am resubscribing to the Observable when it emitted some data successfully.
I am having problems with retryWhen.
For this example I wish to not retry if a ClassCastException occurs.
In my Observable this is not produced which is for a reason because I am testing the logic to retry only on certain errors
However, I am reviving this error with the above code when an error in the Observable is produced
This processor allows only a single Subscriber
I am not sure what is wrong, I have been following this blog post
http://blog.danlew.net/2016/01/25/rxjavas-repeatwhen-and-retrywhen-explained/
Thanks for reading
Let me know if you would like me to post any more details
You are resubscribing to the error flow in your retryWhen which is not allowed and doesn't make sense in your situation. You should delay a value in flatMap instead:
.retryWhen(throwableFlowable -> throwableFlowable.flatMap(
new Function<Throwable, Publisher<?>>() {
#Override
public Publisher<?> apply(Throwable throwable) throws Exception {
if (throwable instanceof ClassCastException) {
return Flowable.error(throwable);
}
return Flowable.just("ignored").delay(INTERVAL, TimeUnit.SECONDS);
}
}
))
I'm using RxSearchView to emit out the results of a search query from an API to a recyclerview. However, if one of those query fails, onError() is called(which is expected) but the subscription as a whole is also canceled. Subsequent queries are not executed at all.
How should i modify the code so that the call to onError() is prevented when a query fails and the next incoming queries are executed normally?
Here's a code snippet:
subscription = RxSearchView.queryTextChanges(searchView)
.debounce(500, MILLISECONDS)
.filter(charSequence -> !TextUtils.isEmpty(charSequence))
.map(CharSequence::toString)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.switchMap(query -> apiService.getSearchResults(query))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<SearchResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(SearchResponse searchResponse) {
if (searchResponse.getStatus().equals("OK")) {
//update Adapter
} else {
//update error views
}
}
});
P.S: I am using switchMap() so that the results of old queries are ignored, if the results of new query has arrived.
You have to handle this error and return an object instead. You can do it, for example, by using onErrorResumeNext operator with apiService.getSearchResults(query) call. What you are going to return - depends on you, you can even return null if you want, but better to create some wrapper which can carry both response status flag and normal response if received.
Something like:
subscription = RxSearchView.queryTextChanges(searchView)
.debounce(500, MILLISECONDS)
.filter(charSequence -> !TextUtils.isEmpty(charSequence))
.map(CharSequence::toString)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.switchMap(query -> apiService
.getSearchResults(query)
.onErrorResumeNext(error -> null)
)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<SearchResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(SearchResponse searchResponse) {
if (searchResponse != null && searchResponse.getStatus().equals("OK")) {
//update Adapter
} else {
//update error views
}
}
});
Of course, this is naive example with using null, in reality you need to write error handling logic. Better to return wrapper, because if using RxJava 2, then it doesn't support null.
I am using RxJava and Retrofit2 (with OkHttp as the HTTP client) to do networking and am trying to understand how different errors are handled by Retrofit2 and how they look from the RxJava side. The following code illustrates an RxJava Subscriber callback for a network call (made with Retrofit).
Subscription subscription = observable
.subscribeOn(mScheduler)
.observeOn(mAndroidScheduler)
.subscribe(new Subscriber<User>() {
#Override
public void onCompleted() {
Timber.d("onCompleted called");
mRetainerView.clearUserObservable();
mActivityView.hideProgressBar();
mActivityView.enableUi();
}
#Override
public void onError(Throwable e) {
Timber.d("onError called");
Timber.d(e.toString());
mRetainerView.clearUserObservable();
mActivityView.hideProgressBar();
mActivityView.enableUi();
}
#Override
public void onNext(User user) {
Timber.d("onNext called");
mRetainerView.clearUserObservable();
mActivityView.hideProgressBar();
mActivityView.enableUi();
mActivityView.launchMainActivity();
}
});
My question is, in what cases will onError() be called and once it's been called, how can I interrogate the Throwable to determine the cause?
From the Retrofit source it looks like the only Throwables that are possible to see are IOException and HttpException. Can anyone verify that that is true?
Here's the basics: onError() will be called if:
the observable you're subscribing to throws an exception (e.g. you get an IOException while trying to read a file)
an exception is raised in your onNext() method.
If there's an exception in your onComplete(), RxJava will propagate an rx.exceptions.OnCompletedFailedException and if there's an exception in onError() - you'll get rx.exceptions.OnErrorFailedException.
That said, you can just probe the Throwable you receive in your onError() method for exceptions that you're expecting. For example you know that if your API call results in client error (4xx), Retrofit will wrap it into HttpException. If there's a timeout with the request you'll get a SocketTimeoutException. Here's a rough example:
#Override
public void onError(Throwable e) {
Timber.d("onError called");
Timber.d(e.toString());
handleError(e);
}
private handleError(Throwable throwable) {
if (throwable instanceof HttpException) {
HttpException httpException = (HttpException)throwable;
int statusCode = httpException.code();
// handle different HTTP error codes here (4xx)
} else if (throwable instanceof SocketTimeoutException) {
// handle timeout from Retrofit
} else if (throwable instanceof IOException) {
// file was not found, do something
} else {
// generic error handling
mRetainerView.clearUserObservable();
mActivityView.hideProgressBar();
mActivityView.enableUi();
}
Do not use onError for flow. That'd be as bad as try-catch for flow.
Error HTTP codes, are valid responses and you should not deal with them in onError.
You can wrap the return type of your Retrofit services in Result, that gives you the means to get information about what happen with your call without throwing exceptions.
You can handle the state of your app using this pattern:
service.getSomething()
.map(r -> Model.success(r.response()))
.onErrorReturn(Model::error)
.observeOn(AndroidSchedulers.mainThread())
.startWith(Resource.loading())
.subscribe(r -> {
myProgressBar.setVisible(r.isLoading());
if (r.isSuccess()) {
handleSuccess(); // e.g. 400 is also success but needs handling
}
if (r.isError()) {
handleError();
}
}, OnErrorNotImplementedException::new);
See how I tried to handle all possible states within the stream and deliberately I throw OnErrorNotImplementedException for something I might've missed. This is very personal but I prefer to crash-fast-and-furious rather than being in an unknown state for a while that later will manifest in a crash harder to debug.
In Kotlin I have used bellow like..
disposable.add(apiService.getLogin_service(parment1,parment1)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSingleObserver<Login_Reg_Data_Model>() {
override fun onSuccess(model: Login_Reg_Data_Model) {
//success
}
override fun onError(e: Throwable) {
if (e is HttpException) {
// We had non-200 http error
Log.e("time exceptionr******>",e.message)
} else if (e is SocketTimeoutException) {
//time exception
Log.e("time exception******>",e.message)
} else if (e is IOException) {
// A network error
Log.e("network error******>",e.message)
} else {
//unknown error
Log.e("unknown error******>",e.message)
}
}
})
)
I have a Button from which I create an Observable<OnClickEvent>.
When the button is clicked, I wish to fetch a file from the network, but I run into issues regarding networking and threads.
This example throws android.os.NetworkOnMainThreadException :
Observable<OnClickEvent> networkButtonObservable = ViewObservable.clicks(testNetworkButton);
networkButtonObservable
.map(new Func1<OnClickEvent, List<String>>() {
#Override
public List<String> call(OnClickEvent onClickEvent) {
return TestAPI.getTestService().fetchTestResponse();
}
}
)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
#Override
public void call(Object o) {Log.w("Final result: " + o);
}
}
);
So I try from another thread.
The following throws rx.exceptions.OnErrorNotImplementedException: Observers must subscribe from the main UI thread, but was Thread[RxNewThreadScheduler-1,5,main] :
networkButtonObservable
.subscribeOn(Schedulers.newThread())
.map(new Func1<OnClickEvent, List<String>>() {
#Override
public List<String> call(OnClickEvent onClickEvent) {
return TestAPI.getTestService().fetchTestResponse();
}
}
)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
#Override
public void call(Object o) {Log.w("Final result: " + o);
}
}
);
Ok.. Now I try with a .debounce() at the start :
networkButtonObservable
.debounce(10, TimeUnit.MILLISECONDS)
.map(new Func1<OnClickEvent, List<String>>() {
#Override
public List<String> call(OnClickEvent onClickEvent) {
return TestAPI.getTestService().fetchTestResponse();
}
}
)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
#Override
public void call(Object o) {Log.w("Final result: " + o);
}
}
);
And this succeeds.
Obviously I do not like to add delays to my code, so I am trying to figure out what's going on, thread-wise. Why is the first example not also executing the code inside the .map() in a background thread?
Or what am I missing here?
--- Update
I change my TestAPI to return an Observable, and change the first call to the networkButtonObservable to .flatMap(). This also functions properly. But I still don't know why the original way using .map() should fail.
networkButtonObservable
.flatMap(new Func1<OnClickEvent, Observable<?>>() {
#Override
public Observable<?> call(OnClickEvent onClickEvent) {
return TestAPI.getTestService().fetchTestResponseObservable();
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
#Override
public void call(Object o) {Log.w("Final result: " + o);
}
}
);
I'm not an expert in Android but based on the error messages, I think you need to bounce the value between the main thread and the background thread. Usually, Android examples show you to add a subscribeOn/observeOn pair to your stream processing:
Observable.just(1)
.map(v -> doBackgroundWork())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(v -> {});
but in these cases, the 'source' is usually a cold observable you are in control.
In your question, the source is a hot Observable with specific requirements that you need to subscribe on the main thread, yet you need to do a network call on a background thread and then show the results on the main thread.
In this case, you can use observeOn multiple times:
networkButtonObservable
.subscribeOn(AndroidSchedulers.mainThread()) // just in case
.observeOn(Schedulers.io())
.map(v -> TestAPI.getTestService().fetchTestResponse())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(v -> updateGUI(v));
I think fetchTestResponseObservable has its own subscribeOn or observeOn applied to it so it doesn't throw the network exception.
Also I'd like to mention that using multiple subscribeOn is functionally equivalent to using only one that is closest to the emitting source, but technically it will hog unused threading resources. Using multiple observeOn in a stream, however, has relevance because you can meaningfully 'pipeline' the stream processing between threads with them.