Retrofit Tutorial

Read my Volley + Gson tutorial: http://www.aimanbaharum.com/2015/01/29/android-development-5-using-volley-and-gson/

App setup

Firstly, we need to import Retrofit library into our app's gradle.build.

compile 'com.squareup.retrofit:retrofit:1.9.0'  

See Retrofit repo for more information

Secondly, add internet permission into Manifest file for we are going to use internet connection in this tutorial.

<uses-permission android:name="android.permission.INTERNET"/>  

Folder structure

/retrofitdemo  
      /rest
          /service
            RecipeApi.java
          /utils
            <utils classes goes here>
          /model
            <our POJO models here>
        RestClient.java
    MainActivity.java

Basic Retrofit Usage

Create a class called RestClient in /rest folder. This class will handle network calling services such as API request. We can also create other objects such as Gson and Http client (using OkHttp) and easily plug it in our Retrofit object.

Below is the class example with minimal usage. See advanced usage section for more usage.

/retrofitdemo/rest/RestClient.java

public class RestClient {

    private static final String API_URL = "http://yourwebsite.io/api/v1";
    // best practice not to put a trailing forward slash in the URL

    private static final RestAdapter REST_ADAPTER = new RestAdapter.Builder()
            .setEndpoint(API_URL)
            .setLogLevel(RestAdapter.LogLevel.FULL) // Log response in full format
            .build();

    private static final RecipeApi RECIPE_APP = REST_ADAPTER.create(RecipeApi.class);

    public static RecipeApi getService() {
        return RECIPE_APP;
    }
}

1. Asynchronous method

/retrofitdemo/rest/service/RecipeApi.java

public interface RecipeApi {

    @GET("/recipe/{id}")
    void getRecipeDetails(@Path("id") String recipeId, Callback<Recipes> response);
}

2. Synchronous method

This method requires AsyncTask to work with. Not recommended

/retrofitdemo/rest/service/RecipeApi.java

public interface RecipeApi {

    @GET("/recipe/{id}")
    RecipeDetails recipeDetails getRecipeDetails(@Path("id") String recipeId);
}

As per Retrofit documentation, @Path("id") annotation in the parameter indicates that {id} in the GET url should be replaced with the recipeId string.

Model (POJO)

In our JSON response, for example:

{
  "results": {
    "user_id": "99401",
    "username": "aimanbaharum",
    "name": "Aiman Baharum",
    "recipes": [
      {
        "id": "1",
        "title": "You w0t m8",
        "content": null
      },
      {
        "id": "2",
        "title": "Top lel",
        "content": "some values"
      }, ...
    ],
    "items": [
      ...
    ]
  },
  "errors": ""
}

By using http://www.jsonschema2pojo.org/ service, we can generate a POJO model out of this JSON response.

Note:
Referring to my Volley tutorial in the Model Class (POJO) section, I've demonstrated on how to write POJO models.

Or perhaps today is your lucky day, I'm just gonna write it here :)

The top level in our JSON response contains results and errors, so we're gonna put that in our ContainerResponse.java class.

model/ContainerResponse.java

// root level

public class ContainerResponse {  

  Results results;

  String errors;

  // create Constructor, Getter/Setter, toString

}

In Results, we have some Strings and two Arrays named recipes and items. Let's create our Results.java class.

model/Results.java

// children of root

public class Results {  

  String user_id;

  @SerializedName("username") String custom_username_variable;

  String name;

  ArrayList<RecipeModel> recipes;

  ArrayList<ItemModel> items;

  // create constructor, getter/setter, toString
}

Next, we're gonna create these two Array classes.

model/RecipeModel.java

// children of Results classs

public class RecipeModel {  

  String id;
  String title;
  String content;

  // create constructor, getter/setter, tostring
}

model/ItemModel.java do it yourself :)

Using Retrofit in Fragments/Activity

1. Asynchronous example

/retrofitdemo/MainActivity.java

...

click.setOnClickListener(new View.OnClickListener() {  
    @Override
    public void onClick(View v) {
        String recipeId = "56"; // can be an EditText string event

        RestClient
          .getService()
          .getRecipeDetails(recipeId, new Callback<ContainerResponse>() {

            @Override
            public void success(ContainerResponse containerResponse, Response response) {

              // containerResponse returns response from the server

              Toast.makeText(context, containerResponse.getResults().getUsername(), Toast.SHORT).show();
            }

            @Override
            public void failure(RetrofitError error) {
              // failed do something
            }

        });
    }
});

2. Synchronous example using AsyncTask

click.setOnClickListener(new View.OnClickListener() {  
    @Override
    public void onClick(View v) {
        String recipeId = "56"; // can be an EditText string event

        new GetRecipeTask().execute(recipeId);
    }
});

private class GetRecipeTask extends AsyncTask<String, Void, ContainerResponse> {  
    @Override
    protected ContainerResponse doInBackground(String... recipeIds) {
        ContainerResponse p = null;
        for (String recipeId : recipeIds) {
            p = RestClient.getService().getRecipeDetails(recipeId);
        }
        return p;
    }

    @Override
    protected void onPostExecute(ContainerResponse result) {
        // response here
    }

}

Advanced Usage

1. Item Type Adapter Factory

Item type adapter is a custom deserializer for deserializing our JSON response. Sometimes we need to use this when our JSON response is different than our model classes. The solution is to make it generic.

Say for an example we only want to take data content in a JSON response:

Excerpt from http://blog.robinchutaux.com/blog/a-smart-way-to-use-retrofit/

{
  "code": 200,
  "error_message": null,
  "data":
  {
      "text": "Hey ! This is a text message :)",
      "value": 4242
  }
}

And create a class that implements TypeAdapterFactory.

public class ItemTypeAdapterFactory implements TypeAdapterFactory {

    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {

        final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
        final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);

        return new TypeAdapter<T>() {

            public void write(JsonWriter out, T value) throws IOException {
                delegate.write(out, value);
            }

            public T read(JsonReader in) throws IOException {

                JsonElement jsonElement = elementAdapter.read(in);
                if (jsonElement.isJsonObject()) {
                    JsonObject jsonObject = jsonElement.getAsJsonObject();
                    if (jsonObject.has("data") && jsonObject.get("data").isJsonObject())
                    {
                        jsonElement = jsonObject.get("data");
                    }
                }

                return delegate.fromJsonTree(jsonElement);
            }
        }.nullSafe();
    }

}

2. Session Request Interceptor

A request interceptor class can be used to add header during API request. This is done programmatically as compared to adding Retrofit annotation to our Api class.

public class SessionRequestInterceptor implements RequestInterceptor {  
    @Override
    public void intercept(RequestFacade request) {
        request.addHeader("Header name", "Header Value");
    }
}

3. OkHttp Connection Timeout

Timeout connection is useful when you need to cancel network request after certain duration.

private static OkHttpClient okHttpClient = new OkHttpClient();  
private static OkHttpClient getOkHttpClient() {  
    okHttpClient.setReadTimeout(60, TimeUnit.SECONDS);
    okHttpClient.setConnectTimeout(60, TimeUnit.SECONDS);
    return okHttpClient;
}

Then construct these classes into our RestClient.java:

private static final String API_URL = "http://yourwebsite.io/api/v1";  
// best practice not to put a trailing forward slash in the URL

// Item Type Adapter Factory
private static Gson gson = new GsonBuilder()  
        .registerTypeAdapterFactory(new ItemTypeAdapterFactory())
        .create();

// OkHttp connection timeout
private static OkHttpClient okHttpClient = new OkHttpClient();  
private static OkHttpClient getOkHttpClient() {  
    okHttpClient.setReadTimeout(60, TimeUnit.SECONDS);
    okHttpClient.setConnectTimeout(60, TimeUnit.SECONDS);
    return okHttpClient;
}

private static final RestAdapter REST_ADAPTER = new RestAdapter.Builder()  
        .setEndpoint(API_URL)
        .setLogLevel(RestAdapter.LogLevel.FULL) // Log response in full format
        .setRequestInterceptor(new SessionRequestInterceptor()) // integrate session request interceptor
        .setConverter(new GsonConverter(gson)) // integrate gson converter
        .setClient(new OkClient(getOkHttpClient())) // integrate okclient
        .build();

private static final RecipeApi RECIPE_APP = REST_ADAPTER.create(RecipeApi.class);

public static RecipeApi getService() {  
    return RECIPE_APP;
}

Source:

Aiman Baharum

More about this blog https://github.com/aimanbaharum/random-wiki/wiki

Kuala Lumpur, Malaysia http://www.aimanbaharum.com

Subscribe to Knowledge Log

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!