I'm using Retofit2 and RxJava2 to fetch data from API. I am using the following code
Observable<Recipe> recipeObservable = getDataManager().getRecipes(String.valueOf(page));
recipeObservable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Recipe>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
}
#Override
public void onNext(#NonNull Recipe recipe) {
getMvpView().updateRecipeList(recipe.getResults());
++page;
//getMvpView().hideLoading();
}
#Override
public void onError(#NonNull Throwable e) {
if (e instanceof HttpException)
Timber.d("Network Error");
}
#Override
public void onComplete() {
}
});
But the problem is Timber.d("Network Error"); never gets executed. I looked on various websites but could not solve the problem. I'm use to Retrofit2 but not fimiliar with RxJava2. So Please help. If more information is required please ask, I'll post it.
HttpException is not related to network failures. IOException is what you are looking for.
Related
I'm using rx libraries im my app to call some REST api on my server and to show the results on screen.
I'm also following the MVP design pattern. So I have a Presenter and an Interactor classes.
In MainInteractor.java I have the following method:
public Observable<Card> fetchCard(final String clientId, final CardFetchedListener listener) {
Log.i(TAG, "FetchCard method");
// Manipulate the observer
return CARDS
.doOnCompleted(new Action0() {
#Override
public void call() {
Log.d(TAG, "CARDS Completed");
}
})
.flatMap(new Func1<Card, Observable<Card>>() {
#Override
public Observable<Card> call(final Card card) {
return ResourceClient.getInstance(card)
.getIDCard()
.observeOn(AndroidSchedulers.mainThread())
.doOnError(new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
Log.w(TAG, "interactor -> fetchCard 2", throwable);
}
}
})
.flatMap(new Func1<CardMeta, Observable<Card>>() {
#Override
public Observable<Card> call(CardMeta cardMeta) {
card.setCardMeta(cardMeta);
saveOrUpdateCardToTheDb(card);
return Observable.just(card);
}
})
.doOnCompleted(new Action0() {
#Override
public void call() {
Log.d(TAG, "Completed body");
}
});
}
});
}
In the logs I can see the "Completed Body" string.
The above method is being called by MainPresenter.java class as follows:
interactor.fetchCard(clientId, this)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Card>() {
#Override
public void onCompleted() {
Log.i(TAG, "fetchCard onCompleted");
view.hideProgressDialog();
view.updateCardsAdapter(cards);
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "Fetch Card error ", e);
onFailure(parseThrowable(e));
}
#Override
public void onNext(Card card) {
if (card != null) {
Log.i(TAG, card.getTenant() + " was fetched and will be displayed");
}
}
});
The problem is that the onCompleted method in the Presenter class is never bein called. I have tried to call onCompleted myself and it worked, but the problem is I don't know actually when the observable has finished emitting cards.
What am I doing wrong here?
UPDATE
CARDS is also an observable that contains meta info. It is initialized using
Observable.from(tenants)
.filter(...).flatMap(// I'm using create operator here and it is calling its onCompleted method successflly);
Is there anyway to retry a retrofit http request when network connection available with Rx-java?
This is my request method
public DisposableObserver<List<Photo>> getNewPhotos(final int page,
int perPage,
String orderBy,
ObservableTransformer<List<Photo>, List<Photo>> observableTransformer,
final Callback<List<Photo>> callBack) {
return photoService.getPhotos(page, perPage, orderBy)
.compose(observableTransformer)
.retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
#Override
public ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Exception {
return null;
}
})
.onErrorResumeNext(new Function<Throwable, ObservableSource<? extends List<Photo>>>() {
#Override
public ObservableSource<? extends List<Photo>> apply(Throwable throwable) throws Exception {
return Observable.error(throwable);
}
})
.subscribeWith(new DisposableObserver<List<Photo>>() {
#Override
public void onNext(List<Photo> value) {
callBack.onSuccess(value);
}
#Override
public void onError(Throwable e) {
callBack.onError(new NetworkError(e));
}
#Override
public void onComplete() {
}
});
}
i think maybe i could do something in retryWhen() method.
i want retrofit to trigger for internet connection and retry the last request when the connection is back.
i know the traditional way to retry but i think there must be a method or something in Rx-java to handle this.
if someone knows its good to share it with me.
The first API call returns a list of elements and I then want to subsequently call another API with a String returned in each element of the list from the first API call. I (think I) have got it so that it's calling the second API call with each element of the list but I am unsure how to then subscribe to that to get the results returned from the second call.
discogsService.getSearchResults(searchTerm, mContext.getString(R.string.token))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
// Turns the result into individual elements
.flatMapIterable(RootSearchResponse::getSearchResults)
// I believe this then calls .getRelease() with each ID string
.map(result -> discogsService.getRelease(result.getId()));
Retrofit Interface:
public interface DiscogsService
{
#GET("database/search?")
Observable<RootSearchResponse> getSearchResults(#Query("q") String searchTerm, #Query("token") String token);
#GET("releases/")
Observable<Release> getRelease(#Query("release_id") String releaseId);
}
I'm unsure where to go from here.
I believe .subscribe(...) then gives me the ability to get the Observable<Release> returned from each .getRelease(...). As the above method is called in the Model layer I then need to set up a subscriber in this model layer to pass back to the Presenter and then an additional subscriber in the Presenter to deal with each Observable as the Presenter has access to the View.
Is there a way so that I can just return each Observable from the Model layer so I don't need to have two separate .subscribe(...)s? Or should I use two separate .subscribe(...)s as I can then catch errors on the both of them? I only want the results from the second call.
Here is the full code that I have tried:
In Model:
discogsService.getSearchResults(searchTerm, mContext.getString(R.string.token))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.flatMapIterable(RootSearchResponse::getSearchResults)
.subscribeOn(Schedulers.io())
.map(result -> discogsService.getRelease(result.getId()))
.subscribe(new Observer<Observable<Release>>()
{
#Override
public void onSubscribe(Disposable d)
{
}
#Override
public void onNext(Observable<Release> value)
{
mainPresenter.addToRecyclerView(value);
}
#Override
public void onError(Throwable e)
{
}
#Override
public void onComplete()
{
}
});
In Presenter:
#Override
public void addToRecyclerView(Observable<Release> value)
{
value .observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<Release>()
{
#Override
public void onSubscribe(Disposable d)
{
}
#Override
public void onNext(Release value)
{
Log.e(TAG, "Success! " + value);
results.add(value);
}
#Override
public void onError(Throwable e)
{
Log.e(TAG, "Error: " + e.toString());
Log.e(TAG, "Error: " + e.toString());
}
#Override
public void onComplete()
{
}
});
I would rather expose an Observable<Release> at model level:
Observable<Release> getReleases(...) {
return discogsService.getSearchResults(...)
.flatMapIterable(RootSearchResponse::getSearchResults)
.flatMap(result -> discogsService.getRelease(result.getId()));
}
Presenter would just subscribe to it:
getReleases
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<Release>()
{
#Override
public void onSubscribe(Disposable d)
{
}
#Override
public void onNext(Release value)
{
Log.e(TAG, "Success! " + value);
results.add(value);
}
#Override
public void onError(Throwable e)
{
Log.e(TAG, "Error: " + e.toString());
Log.e(TAG, "Error: " + e.toString());
}
#Override
public void onComplete()
{
}
});
Only one Observable. Note the switch from map() to flatMap() for the second request in getReleases(...). Behind the scene this is where occur the second subscribe.
The final subscribe will receive errors from both requests. I prefer to let the consumer (Presenter) handle errors, because it's the one who care about the response and know what to do in case of errors (displaying a message for example).
It's the one who 'drive' the Observable, who create, dispose it, so it's also his duty to assign thread imho.
Observable make very good contract to expose from one layer to another. It describe the data type, how to consume it and the pattern (Observable ? Single ? Flowable ?).
This question already has answers here:
retrofit with rxjava handling network exceptions globally
(2 answers)
Closed 6 years ago.
I'm new in RxJava, but I like it. And now I have little problem.
I'm using RxJava + Retrofit.
If I have valid token for user, I get response for API, but if token is invalid, I must refresh token and try make request again.
Valid token:
Make reuqest
Get response
Done
Invalid token:
Make request
Get response
If response_code == 403, need refresh token
Refresh token
Here I want go to 1 step and make request to API again
Else - done
How to make it using RxJava?
Try this one has my knowledge
service.normalRequest()
.flatMap( new Func1<Response, Observable<Response>>() {
#Override
public Observable<Response> call(Response response) {
if (response.code() == 403) {
return service.refreshToken(refreshRequest)
.flatMap(new Func1<Response, Observable<Response>>() {
#Override
public Observable<Response> call(Response response) {
return service.normalRequest();
}
});
} else {
return Observable.just(response);
}
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Subscriber<Response>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Response response) {
}
});
There is a special operator that can be used in this case:
public final Observable<T> retryWhen(final Func1<? super Observable<? extends Throwable>, ? extends Observable<?>> notificationHandler)
If you implement it you will see what parameters it accepts:
observable.retryWhen(new Func1<Observable<? extends Throwable>, Observable<T>>() {
#Override
public Observable<T> call(final Observable<? extends Throwable> error) {
}
});
You can see, that you get the error that was thrown by OkHttp. It is always throwing HttpException, if you call:
httpException.code()
You will get HTTP code number.
So the implementation of the function above might look like:
observable.retryWhen(new Func1<Observable<? extends Throwable>, Observable<T>>() {
#Override
public Observable<T> call(final Observable<? extends Throwable> error) {
return error.flatMap(doRelogging());
}
});
I have made a small library, that does what you expect:
RetrofitRxErrorHandler
You might try it or just look into sources for more advanced retry strategies regarding fetching API errors.
As an example to getting started with RxAndroid I'm trying to implement a searchbox which triggers a rest call when the users inserts something.
So far I have two working parts. The first observing the EditTextView ...
RxTextView.textChangeEvents(searchEditText)
.debounce(400, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<TextViewTextChangeEvent>() {
#Override
public void onCompleted() {
Timber.d("onCompleted");
}
#Override
public void onError(Throwable e) {
Timber.e(e, "onError");
}
#Override
public void onNext(TextViewTextChangeEvent e) {
Timber.d("onNext" + e.text().toString());
}
});
... and the second part calling the REST API by using a Retrofit Service:
APIManager.getService().searchRestaurants("test")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Restaurant>>() {
#Override
public void onCompleted() {
Timber.d("onCompleted");
}
#Override
public void onError(Throwable e) {
Timber.e(e, "onError");
}
#Override
public void onNext(List<Restaurant> restaurants) {
Timber.d("onNext");
for (Restaurant restaurant : restaurants) {
Timber.d(restaurant.getId() + ": " + restaurant.getName());
}
}
});
My Problem is combining the two parts. I tried by using the flatMap Operator as following:
RxTextView.textChangeEvents(searchEditText)
.debounce(400, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Func1<TextViewTextChangeEvent, Observable<List<Restaurant>>>() {
#Override
public Observable<List<Restaurant>> call(TextViewTextChangeEvent txtChangeEvt) {
return APIManager.getService().searchRestaurants(txtChangeEvt.text().toString());
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Restaurant>>() {
#Override
public void onCompleted() {
Timber.d("onCompleted");
}
#Override
public void onError(Throwable e) {
Timber.e(e, "onError");
}
#Override
public void onNext(List<Restaurant> restaurants) {
Timber.d("onNext");
for (Restaurant restaurant : restaurants) {
Timber.d(restaurant.getId() + ": " + restaurant.getName());
}
}
});
When I do this I get following exception:
java.lang.IllegalStateException: Must be called from the main thread. Was: Thread[RxCachedThreadScheduler-1,5,main]
at com.jakewharton.rxbinding.internal.Preconditions.checkUiThread(Preconditions.java:28)
at com.jakewharton.rxbinding.widget.TextViewTextChangeEventOnSubscribe.call(TextViewTextChangeEventOnSubscribe.java:21)
at com.jakewharton.rxbinding.widget.TextViewTextChangeEventOnSubscribe.call(TextViewTextChangeEventOnSubscribe.java:12)
So I tried to fix that by calling subscribeOn(AndroidSchedulers.mainThread() but in this case, of course, I get an NetworkOnMainThread Exception.
So how Do I do this?
What is a proper way to combine different Observables which should execute on different Threads?
Just remove the first .observeOn(AndroidSchedulers.mainThread()). Take a look at this example
Observable.just(1) // 1 will be emited in the IO thread pool
.subscribeOn(Schedulers.io())
.flatMap(...) // will be in the IO thread pool
.observeOn(Schedulers.computation())
.flatMap(...) // will be executed in the computation thread pool
.observeOn(AndroidSchedulers.mainThread())
.subscribe(); // will be executed in the Android main thread (if you're running your code on Android)