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:

  1. We will have to handle onError() function on every request
  2. 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 requests
  • 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