Simple Android REST calls with Retrofit2

It’s often necessary for your app to communicate with a RESTful server. In a current project, we chose to use Retrofit2, a library developed by Square. In this post, I’ll discuss the basics of getting started with Retrofit2, as well as a few tricks I’ve found useful when using it.

The Basics

Retrofit2 is a flexible library that uses annotated interfaces to create REST calls. To get started, let’s look at an example that makes a GET request for books. Here’s the Book class we’ll be using:


public class Book {
    private String author;
    private String title;

    public String getAuthor() {
        return author;
    }

    public String getTitle() {
        return title;
    }
}

The service interface

Once we’ve defined the class, we can make a service interface to handle our API. A GET request to load all books could look something like this:


public interface BookService {
    @GET("books")
    Call<List<Book>> getAllBooks();
}

Note that the @GET annotation takes the endpoint we wish to hit. As you can see, an implementation of this interface will return a Call object containing a list of books.

Calling the API

So how do we use this interface to make requests to the API? Use Retrofit2 to create an implementation of the above interface, and then call the desired method. Retrofit2 supports a number of converters used to map Java objects to the data format your server expects (JSON, XML, etc). I’ll be using the Gson converter.


// create an instance of gson to be used when building our service
Gson gson = new GsonBuilder().create();

// use retrofit to create an instance of BookService
BookService service = new Retrofit.Builder()
        .baseUrl(URL)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build()
        .create(BookService.class);

// call the method we defined in our interface, and handle the result
service.getAllBooks().enqueue(new Callback>() {
    @Override
    public void onResponse(Call<List<Book>> call, Response<List<Book>> response) {
        System.out.println(response.code());

        List books = response.body();
        // do something with books here
    }

    @Override
    public void onFailure(Call<List<Book>> call, Throwable t) {
        System.out.println("Something went wrong!");

    }
});

Retrofit.Builder is used to create the instance of BookService. In the above example, there are a few things to note:

  • A base URL is provided to the builder, and the endpoints specified in the interface method will be appended to this.
  • An optional converter can be provided.
  • The create method takes the interface class.

Next, the desired method is called on the service-in this case, getAllBooks. The returned Call object has a few useful methods. The one I am using is enqueue, which allows for asynchronous callbacks. If you prefer synchronous behavior, execute will work.

In the case of enqueue, we must provide an instance of Callback. Its first method, onResponse, handles successful calls, and onFailure handles failures–typically related to network issues. When onResponse is called, it gives us a Response object, which has two important methods:

  • code: the response code
  • body: the thing we requested

Other Types of Calls

Of course, Retrofit2 is not limited to GET requests. You may specify other REST methods using the appropriate annotations (such as @POST, @PATCH, and @DELETE).

An example POST call could look like this:


@POST("books")
Call<Book> saveBook(@Body Book book);

Note that the data to be sent is prefixed by the @Body annotation.

If we wanted to request a book by ID, we could write the following method in our service class:


@GET("books/{id}")
Call<Book> getBook(@Path("id") int id);

Headers

If you wish to add headers to your calls, you can annotate your method or arguments to indicate this. For instance, if we wanted to add a content type and a user’s authentication token, we could do this:


@POST("books")
@Headers({"Content-Type: application/json"})
Call<Book> saveBook(@Body Book book, @Header("Authorization") String token);

Bridging the Gap Between Android Code and Your API

Variable names

In the examples above, we assumed that there was an exact mapping of instance variable names between the the Book class and the server. This will often not be the case, especially if your server uses a different spelling convention than your Android app.

For example, if you use a Rails server, you will probably be returning data using snake_case, while your Java probably uses camelCase. If we add datePublished to the Book class, we may be receiving it from the server as date_published.

To create this mapping, use the @SerializedName annotation on the instance variable. For example:


@SerializedName("date_published")
private Date datePublished;

Date formats

Another potential disconnect between the app and the server is the way they represent date objects. For instance, Rails may send a date to your app in the format "yyyy-MM-dd'T'HH:mm:ss," which Gson will not be able to convert to a Java Date. We have to explicitly tell the converter how to perform this conversion. In this case, I altered the Gson builder call to look like this:


Gson gson = new GsonBuilder()
                .setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
                .create();

Now our dates are correctly mapped!

I hope this was helpful. Leave a comment if you have any tips for using Retrofit2 more effectively.

Conversation
  • Josiah Campbell says:

    Retrofit2 supports request interceptors when building the Retrofit instance. This is helpful if you have universal headers such as a client key.

    Another feature with Gson is the support for alternate serialization keys. For instance, if an API returns book with a key “id”, you can provide an alternate if “book_id” represents the same field.

  • Binh Thanh Nguyen says:

    Thanks, nice post

  • Comments are closed.