RxJava2 and Retrofit2 Error Handling on a single place
This article is about handling Errors on a single place (example uses MVP architecture) using RxJava2 and Retrofit2. It won’t cover Rx, Retrofit or MVP architecture as there are a lot of good examples out there, instead it is focused only on a Error handling strategy through DisposableObserver.
Suppose we have a request in `Retrofit2` as below:
@GET("search/images")
Observable<GetImagesResponse> getImages(
@Query("page") int page,
@Query("page_size") int pageSize,
@Query("phrase") String phrase
);
Usually when making such a request we handle it as following:
getImages(pageNo, pageCount, phrase)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<GetImagesResponse>() {
@Override
public void onNext(GetImagesResponse getImagesResponse) {
//Handle logic
}
@Override
public void onError(Throwable e) {
//Handle error
}
@Override
public void onComplete() {
}
});
However if we do so, we will face two problems that will affect readability and maintainability of the code:
- We will have to handle onError() function on every request
- We will implement onComplete() function, even though we won’t use it
To avoid this cases we will create a generic abstract class that will work as a Response wrapper:
public abstract class CallbackWrapper <T extends BaseResponse> extends DisposableObserver<T>{ //BaseView is just a reference of a View in MVP
private WeakReference<BaseView> weakReference;
public CallbackWrapper(BaseView view) {
this.weakReference = new WeakReference<>(view);
}
protected abstract void onSuccess(T t);
@Override
public void onNext(T t) {
//You can return StatusCodes of different cases from your API and handle it here. I usually include these cases on BaseResponse and iherit it from every Response
onSuccess(t);
}
@Override
public void onError(Throwable e) {
BaseView view = weakReference.get();
if (e instanceof HttpException) {
ResponseBody responseBody = ((HttpException)e).response().errorBody();
view.onUnknownError(getErrorMessage(responseBody));
} else if (e instanceof SocketTimeoutException) {
view.onTimeout();
} else if (e instanceof IOException) {
view.onNetworkError();
} else {
view.onUnknownError(e.getMessage());
}
}
@Override
public void onComplete() {
}
private String getErrorMessage(ResponseBody responseBody) {
try {
JSONObject jsonObject = new JSONObject(responseBody.string());
return jsonObject.getString("message");
} catch (Exception e) {
return e.getMessage();
}
}
}
Then you can implement it like so:
getImages(pageNo, pageCount, phrase)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new CallbackWrapper<GetImagesResponse>(view) {
@Override
protected void onSuccess(GetImagesResponse getImagesResponse) {
//Handle your logic here
}
};
This way you:
- won’t have to implement functions you don’t need
- won’t have to handle errors in every request
- will reduce boilerplate code
- will have more maintainable and readable code
An implementation example of this approach can be found here
If you liked this article, be sure to click ❤ below and check my Play Store Apps