Creating maintainable Android API clients

Automatically parse REST APIs as Java objects with Retrofit

  • 28th August 2015

If you have ever tried downloading and parsing JSON data on Android, you might know how much of a pain it can be. If you use HttpUrlConnection, a typical request might look like the following:

HttpURLConnection c = null;

URL u = new URL("https://codeship.com/api/v1/api/v1/projects/" + projectId + ".json");
c = (HttpURLConnection) u.openConnection();
c.setRequestMethod("GET");
c.setConnectTimeout(5);
c.setReadTimeout(5);
c.connect();
int status = c.getResponseCode();

InputStream in = c.getInputStream();

// etc
...

All that is without parsing the status codes and catching the many exceptions that could be thrown. Even then, you still need to parse the JSON for each individual key.

Enter Retrofit

Retrofit is "A type-safe REST client for Android and Java".

Although Retrofit is not a new tool, it is very powerful and makes a huge difference when developing and maintaining a project. Add it as a dependency of your project with compile 'com.squareup.retrofit:retrofit:1.9.0'.

Lets take the Codeship API as an example:

A list of all builds for a project can be retrieved from the endpoint GET /api/v1/projects/:project_id.json. A CodeshipService interface can be created, which defines the method for this API route, like below:

public interface CodeshipService {
    @GET("/projects/{project_id}.json")
    void getProject(@Path("project_id") String projectId, Callback<Project> cb);
}
  • The @GET part is called an annotation. This provides a form of metadata indicating how the request should be handled.
  • The {brackets} indicate that this part of the URL Path can be replaced, using the @Path annotated parameter below.
  • The callback means that the request will be executed asynchronously.
  • <Project> represents the object that the JSON response will be converted into.

The Codeship documentation defines the JSON structure of a project as follows:

{
  "id": 33837,
  "repository_name": "codeship/documentation",
  "uuid": "59a737f0-1648-0132-c4e7-72c6c37b1f6e",
  "builds": []
}

this can be represented as a Java object (POJO):

public class Project {
    public String id;
    public String uuid;
    public List<Build> builds;

    public String getId() {
        return id;
    }

    public List<Build> getBuilds() {
        return builds;
    }
}

Once the interface and entities have been created and all the API routes that will be used have been defined, it can be implemented into RETROFIT adapter. I recommend creating a custom class to wrap the service, this makes configuration much easier, and reusable.

public class Codeship {

    public CodeshipService service;

    public Codeship(final String apiKey) {

        // Create the restAdapter, setting the base url
        RestAdapter restAdapter = new RestAdapter.Builder()
            .setEndpoint("https://codeship.com/api/v1")
            // Codeship requires an API key, so add this in to each request
            .setRequestInterceptor(new RequestInterceptor() {
                @Override
                public void intercept(RequestInterceptor.RequestFacade request) {
                    request.addQueryParam("api_key", apiKey);
                }
            })
            .build();

        this.service = restAdapter.create(CodeshipService.class);
    }
}

The following method can then use the interface created earlier:

public void getProject(String projectId, Callback<Project> cb) {
   service.getProject(projectId, cb);
}

Tying it all together

Now that all the pieces have been created, we can make use of our new API service:

// Create the Codeship service
Codeship cs = new Codeship(apiKey);

cs.getProject("123123", new Callback<Project>() {
    @Override
    public void success(Project project, Response response) {
        // Update the UI and show the project information

        List<Build> builds = project.getBuilds();
    }

    @Override
    public void failure(RetrofitError error) {
        // Handle the error and show an appropriate message to the user
    }
});

This code can be easily reused all over your application, without needing to set options each time. If Codeship decides to make a change to their API routes, the interface and service can be quickly changed without affecting the rest of your code.

Retrofit 2.0 is due out by the end of 2015 and promises to bring a host of new features and improvements. Version 2.1 should also bring support for websockets. [^1]

*Edit (2016): Since writing this article, Retrofit 2.0 has been released, I recommend reading this article for an overview of the new features and syntax changes.*

[^1]: Source: https://speakerdeck.com/jakewharton/simple-http-with-retrofit-2-droidcon-nyc-2015