Retrofit 2 — Simple Error Handling

Two weeks ago, you’ve seen how to log requests and responses for debugging purposes. Requests might not finish successfully and you have to take care of failure situations. Most of the time, you need to manually apply the correct action like showing an error message as user feedback. If you get more than just the response status code, you can use the additional data to set the user in the right context and provide more information about the current error situation. That’s what this post is about: how to apply simple error handling using Retrofit 2.

Retrofit Series Overview

  1. Introduction to Call Adapters (Coming soon)
  2. Custom Call Adapter to Separate OnResponse Callback (Coming soon)
  3. How to Integrate RxJava 1.x Call Adapter (Coming soon)
  4. How to Integrate RxJava 2.x Call Adapter (Coming soon)
  5. How to Integrate Guava Call Adapter (Coming soon)
  6. Custom Call Adapter to Separate Network and Gson Errors (Coming soon)
  1. Callbacks (Coming soon)
  2. Annotations (Coming soon)
  3. Fluent Interface with Builders (Coming soon)

Error Handling Preparations

Even though you want your app to always work like expected and there shouldn’t be any issues while executing requests. However, you’re not in control of when servers will fail or users will put wrong data which results in errors returned from the requested API. In those cases, you want to provide as much feedback to the user required to set him/her into the right context so that he/she understands what the issue is.

Before diving into the actual request which results in an error, we’re going to prepare classes to parse the response body which contains more information.

Error Object

At first, we create the error object representing the response you’re receiving from your requested API. Let’s assume your API sends a JSON error body like this:

{
    statusCode: 409,
    message: "Email address already registered"
}

If we would just show the user a generic error message like There went something wrong, he/she would immediately be upset about this stupid app which isn’t able to show what went wrong.

To avoid these bad user experiences, we’re mapping the response body to a Java object, represented by the following class.

public class APIError {

    private int statusCode;
    private String message;

    public APIError() {
    }

    public int status() {
        return statusCode;
    }

    public String message() {
        return message;
    }
}

We don’t actually need the status code inside the response body, it’s just for illustration purposes and this way you don’t need to extra fetch it from the response.

Simple Error Handler

We’ll make use of the following class only having one static method which returns an APIError object. The parseError method expects the response as parameter. Further, you need to make your Retrofit instance available to apply the appropriate response converter for the received JSON error response.

public class ErrorUtils {

    public static APIError parseError(Response<?> response) {
        Converter<ResponseBody, APIError> converter = 
                ServiceGenerator.retrofit()
                        .responseBodyConverter(APIError.class, new Annotation[0]);

        APIError error;

        try {
            error = converter.convert(response.errorBody());
        } catch (IOException e) {
            return new APIError();
        }

        return error;
    }
}

We’re exposing our Retrofit instance from ServiceGenerator via static method (if you’re not familiar with the ServiceGenerator, please read the introductory post of this series). Please bear with us that we’re using a kind of hacky style by exposing the Retrofit object via static method. The thing that is required to parse the JSON error is the response converter. And the response converter is available via our Retrofit object.

At first, we’re getting the error converter from the ServiceGenerator.retrofit() instance by additionally passing our APIError class as the parameter to the responseBodyConverter method. The responseConverter method will return the appropriate converter to parse the response body type. In our case, we’re expecting a JSON converter, because we’ve received JSON data.

Further, we call converter.convert to parse the received response body data into an APIError object. Afterwards, we’ll return the created object.

Error Handler in Action

Retrofit 2 has a different concept of handling "successful" requests than Retrofit 1. In Retrofit 2, all requests that can be executed (sent to the API) and for which you’re receiving a response are seen as "successful". That means, for these requests the onResponse callback is fired and you need to manually check whether the request is actual successful (status 200-299) or erroneous (status 400-599).

If the request finished successfully, we can use the response object and do whatever we wanted. In case the error actually failed (remember, status 400-599), we want to show the user appropriate information about the issue.

Call<User> call = service.me();  
call.enqueue(new Callback<User>() {  
    @Override
    public void onResponse(Call<User> call, Response<User> response) {
        if (response.isSuccessful()) {
            // use response data and do some fancy stuff :)
        } else {
            // parse the response body …
            APIError error = ErrorUtils.parseError(response);
            // … and use it to show error information

            // … or just log the issue like we’re doing :)
            Log.d("error message", error.message());
        }
    }

    @Override
    public void onFailure(Call<User> call, Throwable t) {
        // there is more than just a failing request (like: no internet connection)
    }
});

As you can see, we use the ErrorUtils class to parse the error body and get an APIError object. Use this object and the contained information to show a meaningful message instead of a generic error message.

Outlook

This article shows you a simple way to manage errors and extract information from the response body. Most APIs will send you specific information on what went wrong and you should make use of it.

This is just the tip of the iceberg when it comes to error handling. Within Retrofit 1, you had the opportunity to add a custom error handler. This option was removed from Retrofit 2 and we think it’s good the way it is. We’ll tell you about more advanced techniques on error handling with Retrofit 2 within a future blog post.

If you run into any issue or have a question, please let us know in the comments below or tweet us @futurestud_io.


Explore the Library

Find interesting tutorials and solutions for your problems.

Miscellaneous