Android Retrofit Tutorial


Photo Credit: Stian Eikeland - Android Retrofit Tutorial

A lot of people have been asking me about Retrofit. How it can be used in Android. What does it do etc. As per its official documentation, Retrofit is nothing but a networking library for java and Android by Square inc., under Apache 2.0 license. By using Retrofit in Android we can seamlessly capture JSON responses from a web API. It is different from other libraries because, Retrofit gives us an easy to use platform through which we don’t need to parse JSON responses as they are done by the library itself. It uses the GSON library in background to parse the responses. All we need to do is define a POJO(Plain Old Java Object) to do all the parsing. In this Android Retrofit Tutorial we will discuss the basic scenarios, where it can be used.

Android Retrofit Example

To use Retrofit in Android, minimum version required is 2.3 and Java 6. What makes Retrofit different is that it gives a unique feature through which we can define java interfaces with annotations, which can be further used to send parameters. These interfaces are the only access points in our code through which we will be making REST web API calls. The process of making a REST web API call through Retrofit in Android is simple, first all these dependencies should be added in the build.gradle(app) :

implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'

Next a internet permission should be added in the manifest:

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

For this Android Retrofit Tutorial, we are showing responses in a text view, hence a layout with the text view should be created:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:paddingBottom="@dimen/activity_vertical_margin"
                android:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin"
                tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</RelativeLayout>

For this Android Retrofit example, we will be using a REST web API from https://freemusicarchive.org/api. We will be discussing curators method of this API, which just requires an api_key as a query parameter. Therefore first lets make a POJO for the data we need:

package com.truiton.retrofitexample;

import java.util.List;

public class Curator {
    public String title;
    public List<Dataset> dataset;

    public class Dataset{
        String curator_title;
        String curator_tagline;
    }
}

To parse the response properly you may need to define the above class carefully. As it should have the same structure as JSON being returned in response. Although it is not required to define all the fields, if you don’t want to parse all of them. Next lets make a java interface to access the web service:

package com.truiton.retrofitexample;

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;

public interface IApiEndPoint {

    @GET("/api/get/curators.json")
    Call<Curator> getCurators(@Query("api_key") String key);

}

In this Android Retrofit interface above you may notice that, two custom annotations have been used. The @Get annotation is used to define the relative HTTP URL path and method. While the @Query annotation is used to define the query string for URL in discussion. The above interface would be making a web API hit on the following URL:

http://freemusicarchive.org/api/get/curators.json?api_key=-------

Creating a Retrofit client with Okhttp client

Further to actually make a request we need to create a retrofit client object. But according to the new Retrofit’s architecture, we don’t need to create a new Retrofit client object every time. Instead we can use a singleton pattern. This way we can save a large amount of memory space by not instantiating an object every time a request is being made. Also now days we do not need to add a dependency explicitly for OkHttp client. By adding a dependency for retrofit, it automatically adds the appropriate version of OkHttp client to the project. For this project we are using Retrofit 2.4.0 which has the default OkHttp version 3.10.0. Now going forward we need to create a Retrofit client singleton object.

Android Retrofit logging Interceptor

Often when making HTTP request, we tend to check for request and response logs. That is, what was the actual request made and what was the actual response that was returned. To do so, we first need to add a logging interceptor dependency in the build.gradle (app) file:

implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'

After this we can define our method to generate a basic android retrofit client which has the support for request and response log.

package com.truiton.retrofitexample;

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitClient {
    private static final String API_URL = "http://freemusicarchive.org";
    public static final String API_KEY = "----------";
    private static Retrofit basicClient;

    public synchronized static Retrofit getBasicClient() {
        if (basicClient == null) {
            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            logging.setLevel(HttpLoggingInterceptor.Level.BODY);
            OkHttpClient client = new OkHttpClient.Builder()
                    .addInterceptor(logging)
                    .build();
            basicClient = new Retrofit.Builder()
                    .baseUrl(API_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .client(client)
                    .build();
        }
        return basicClient;
    }

    public static IApiEndPoint getApiEndPoint() {
        return getBasicClient().create(IApiEndPoint.class);
    }
}

Now that in the above sections of this Android Retrofit Tutorial we have defined a basic structure of how retrofit library can be used to make HTTP requests. We can move towards the usage of it.

Android Retrofit Asynchronous requests: Background processing

As you may know Android does not allow network interaction on main thread. It throws a NetworkOnMainThreadException. It is suggested to do all the network processing either in background or in worker threads. Retrofit library is powerful enough to support this. In this Android Retrofit Tutorial we will show how to make asynchronous background requests through Retrofit.

To do so, we simply need to call the Call.enqueue() method, this way retrofit itself sends the request asynchronously to the server.

package com.truiton.retrofitexample;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;


public class MainActivity extends AppCompatActivity {

    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textView);

        IApiEndPoint methods = RetrofitClient.getApiEndPoint();
        Call<Curator> call = methods.getCurators(RetrofitClient
                .API_KEY);
        call.enqueue(new Callback<Curator>() {
            @Override
            public void onResponse(Call<Curator> call, Response<Curator> response) {
                Curator curators = response.body();
                textView.setText(curators.title + "\n\n");
                for (Curator.Dataset dataset : curators.dataset) {
                    textView.setText(textView.getText() + dataset.curator_title +
                            " - " + dataset.curator_tagline + "\n");
                }
            }

            @Override
            public void onFailure(Call<Curator> call, Throwable t) {

            }
        });
    }
}

So to make a successful network call through Android retrofit library first we need to create an instance of retrofit client. This instance would be used to execute all the requests in future. Next we created an instance of API methods interface IApiEndPoint. These methods will further return the parsed response, which would be used in our app.

Android Retrofit Synchronous Call Example

In this section we will show an example of Android retrofit synchronous requests. Here we will make a network request synchronously without crashing the app. But since android does not allow network interaction on main thread, we would use an AsyncTask here, but only for demo purposes (in production, better approaches can be used). One of the differences from previous approach is that, here Call.execute() method is used. To make a sample with it please have a look at the code below:

package com.truiton.retrofitexample;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import java.io.IOException;
import retrofit2.Call;

public class MainActivitySynchronous extends AppCompatActivity {

    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textView);

        BackgroundTask task = new BackgroundTask();
        task.execute();
    }

    private class BackgroundTask extends AsyncTask<Void, Void,
            Curator> {
        Call<Curator> call;
        @Override
        protected void onPreExecute() {
            IApiEndPoint methods = RetrofitClient.getApiEndPoint();
            call = methods.getCurators(RetrofitClient.API_KEY);
        }

        @Override
        protected Curator doInBackground(Void... params) {
            Curator curators = null;
            try {
                curators = call.execute().body();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return curators;
        }

        @Override
        protected void onPostExecute(Curator curators) {
            textView.setText(curators.title + "\n\n");
            for (Curator.Dataset dataset : curators.dataset) {
                textView.setText(textView.getText() + dataset.curator_title +
                        " - " + dataset.curator_tagline + "\n");
            }
        }
    }
}

You may have noticed by now in this Android Retrofit Tutorial that requests are being made in an AsyncTask. Above example shows how to make synchronous Retrofit request. Here Retrofit does not do processing in background, hence if you try to do so, it will give you a NetworkOnMainThreadException. If you wish to do asynchronous requests: all the processing in background; then read the previous section.

Android Retrofit Example

More on Retrofit in Android

Retrofit supports a wide variety of request methods and parameters. Recently it has even started supporting caching on HTTP responses. In this tutorial section we will discuss all this and some of the important request interface example types. Also you can access full source code for this Android Retrofit Tutorial here:

Source Code

Android Retrofit HTTP GET Method:

As shown in above examples, you can use get method to make a REST call.

@GET("/api/get/curators.json")
Call<Curator> getCurators(@Query("api_key") String key);

Android Retrofit HTTP POST Method:

With Retrofit, post requests can also be made

@POST("relative-post-path.json")
Curator getCurators(@Query("api_key") String key);

Android Retrofit Dynamic URL Creation:

Retrofit allows us to manipulate URLs dynamically by using { } brackets in the request methods. Values in these brackets are populated using the @path annotation.

    @GET("/get/{dataset}.{format}")
    Curator getCurators(@Path("dataset") String dataset,
                        @Path("format") String format,
                        @Query("api_key") String key);

Android Retrofit Headers:

You can also modify the headers for your request.

@Headers({"Accept: application/vnd.github.v3.full+json",
            "User-Agent: Android-Retrofit-Tutorial"})
@GET("/get/curators.json")
Curator getCurators(@Query("api_key") String key);

Android Retrofit Cache Example

In newer versions of Retrofit, latest OkHttp client is bundled, which supports caching. Implementing this is also not very difficult. We just need to modify the way we are building our retrofit client to look like this:

    public synchronized static Retrofit getClientWithCaching(Context ctx) {
        if (clientWithCaching == null) {
            int cacheSize = 10 * 1024 * 1024; // 10 MB
            Cache cache = new Cache(ctx.getCacheDir(), cacheSize);

            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            logging.setLevel(HttpLoggingInterceptor.Level.BODY);
            OkHttpClient client = new OkHttpClient.Builder()
                    .addInterceptor(logging)
                    .cache(cache)
                    .build();

            Retrofit.Builder builder = new Retrofit.Builder()
                    .baseUrl(API_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .client(client);
            clientWithCaching = builder.build();
        }
        return clientWithCaching;
    }

Once this is done, you can make a network request as shown in above sections but with this new retrofit cache enabled client.

Please Note: If you still believe that in your case responses are not being cached, please check the response headers. It should have caching headers. Otherwise you would have to do it manually.

How to do centralized 401 error handling and retry in retrofit 2?

If for example you are building an enterprise level app and wish to ensure that your app handles the 401 unauthorized errors as well for all the rest calls. You can now do so very easily with Retrofit 2. As now with retrofit 2 you may not need to write extensive amount of code for this implementation. In reality to actually make a synchronous 401, 422 etc. retry call  using Retrofit 2 you just need to implement an interceptor. By using this interceptor you can actually check for a response code and based on the error you can make a synchronous retry call. Please have a look at the code:

    public synchronized static Retrofit getClientWithRetry(final Context ctx) {
        if (clientWithRetry == null) {
            Interceptor responseCodeInterceptor = new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request request = chain.request();
                    Response response = chain.proceed(request);
                    if (response.code() == 401) {
                        Log.d(LOG_TAG, "Intercepted Req: " + response.toString());
                        Response r = retryWithFreshToken(request, chain);
                        return r;
                    }
                    return response;
                }
            };

            int cacheSize = 10 * 1024 * 1024; // 10 MB
            Cache cache = new Cache(ctx.getCacheDir(), cacheSize);

            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            logging.setLevel(HttpLoggingInterceptor.Level.BODY);
            OkHttpClient client = new OkHttpClient.Builder()
                    .addInterceptor(logging)
                    .addInterceptor(responseCodeInterceptor)
                    .cache(cache)
                    .build();

            Retrofit.Builder builder = new Retrofit.Builder()
                    .baseUrl(API_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .client(client);
            clientWithRetry = builder.build();
        }
        return clientWithRetry;
    }

    private static Response retryWithFreshToken(Request req, Interceptor.Chain
            chain) throws IOException {
        Log.d(LOG_TAG, "Retrying with new token");
        String newToken = refreshToken();
        Request newRequest;
        newRequest = req.newBuilder().header("Authorization", " Token " + newToken).build();
        return chain.proceed(newRequest);
    }

    private static String refreshToken() throws IOException {
        IApiEndPoint methods = RetrofitClient.getBasicClient().create(IApiEndPoint.class);
        Call<Curator> call = methods.getCurators(RetrofitClient.API_KEY);
        return call.execute().body().toString();
    }

As you can see above how to retry a retrofit request based on response code. Since this is implemented through interceptors this can act as a centralized request queueing and retrying architecture for an app. Here in this short android retrofit example above simply after detecting the response code a synchronous request is made and after that, the original request is made with new Authorization token. 

Retrofit is a simple library, which can easily turn your app into a powerful REST Client. It supports the header modification, dynamic URL modification. Also it gives all the HTTP methods like POST, GET, PUT, DELETE, and HEAD. You can make synchronous as well as asynchronous web API calls through it. Hope this Android Retrofit Tutorial helped you. Connect with us on Facebook, Google+, and Twitter for more updates.

About Mohit Gupt

An android enthusiast, and an iPhone user with a keen interest in development of innovative applications.


Leave a comment

Your email address will not be published. Required fields are marked *

3 thoughts on “Android Retrofit Tutorial