Consuming REST API using Retrofit Library in Android

Hi Android beginner developers, in this tutorial, i’ll introduce you to the Retrofit Library and also try to provide a step by step guide on how to consume JSON objects using the retrofit library. This practice requires that you are familiar with Java and Android App development. For those interested in learning the very basics of Java, check my blog for some very old articles i wrote on Introduction to Java

In this tutorial, we are going to create an Android App that uses the Retrofit Library to download JSON Objects containing movie details from The Movie DB API, this movie details will now be displayed in a RecyclerView on the Android App.

Find the full source code for the project here on GitHub.

What is Retrofit?

Retrofit is a REST Client library (Helper Library) used in Android and Java to create an HTTP request and also to process the HTTP response from a REST API. It was created by Square, you can also use retrofit to receive data structures other than JSON, for example SimpleXML and Jackson. Before we continue, let’s briefly define REST Client and REST API in our context.

REST Client in our case is the Retrofit library that is used on the client side (Android) to make HTTP request to REST API, in our case, The Movie DB API and also process the response.

A REST API defines a set of functions which developers can perform requests and receive responses via HTTP protocol such as GET and POST. in our case, The Movie DB (TMDB) API is the REST API.

We can also simply say that a RESTful API is an application program interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data.

To use Retrofit in your Android Application, you’ll need 3 major classes.

  1. An Interface which defines the HTTP operations (Functions or methods)

According to Square, creators of Retrofit documentation, Retrofit turns your HTTP API into a Java interface. Sample codes for the interface and the method declared in it are as below:

public interface GitHubService {@GET("users/{user}/repos")Call<List<Repo>> listRepos(@Path("user") String user);@GET("group/{id}/users")Call<List<User>> groupList(@Path("id") int groupId);}

Every method inside an interface represents one possible API call. It must have a HTTP annotation (GET, POST, etc.) to specify the request type and the relative URL. The return value wraps the response in a Call object with the type of the expected result.

Query parameters can also be added to a method.

@GET(“group/{id}/users”)Call<List<User>> groupList(@Path(“id”) int groupId, @Query(“sort”) String sort);

You can use replacement blocks and query parameters to adjust the URL. A replacement block is added to the relative URL with {}. With the help of the @Path annotation on the method parameter, the value of that parameter is bound to the specific replacement block.

2. A Retrofit class which generates an implementation of the GitHubService interface. The below sample code would be inside the Retrofit class and this is how it creates an instance of Retrofit and implements the listRepos() method that’s in the GitHubService Interface.

Retrofit retrofit = new Retrofit.Builder().baseUrl(“https://api.github.com/").build();GitHubService service = retrofit.create(GitHubService.class);Call<List<Repo>> repos = service.listRepos(“Gino Osahon”);

3. The last of the 3 needed class is a simple POJO that matches each field in the JSON response object gotten from querying an API. It’s a simple class with getter and setter methods for each fields. We’ll see sample codes later.

Retrofit Converters

Retrofit Converters are like an agreement between and Android client and the Server on the format on which data will be represented. Both parties can agree that for our communication, the format for data transfer will be JSON, as in our case in this tutorial. Remember i said apart from the JSON structure converter, we have others and here are some supported by Retrofit.

Gson:

Gson is for JSON mapping and can be added with the following dependency:

compile ‘com.squareup.retrofit2:converter-gson:2.2.0’

SimpleXML

SimpleXML is for XML mapping. You’ll need the following line for your build.gradle:

compile ‘com.squareup.retrofit2:converter-simplexml:2.2.0’

Jackson

Jackson is an alternative to Gson and claims to be faster in mapping JSON data. The setup offers you a lot more customization and might be worth a look. You can add it with:

compile ‘com.squareup.retrofit2:converter-jackson:2.2.0’

Moshi

Moshi is another alternative to Gson. It’s created by the developers of Retrofit. Moshi is based on Gson, but differentiates itself with some simplifications. If you want to give this young new player on the market a try, add it with:

compile ‘com.squareup.retrofit2:converter-moshi:2.2.0’

Retrofit supports Authentication for API calls that require the user to be authenticated before using the API to access resources. Querying Twitter, Facebook and StackoverFlow all require authentication. However, authentication feature is out of the scope of this tutorial as we’ll simply be querying The Movie DB(TMDB) API which requires no authentication.

Hmm! Enough talk, now It’s time to write some codes. Let’s now create an Android App using Android Studio. The App uses Retrofit to fetch Movie details from “The Movie DB” website and then displays these details on the Android Client RecyclerView Activity.

We’ll be using the TMDB API, So the first step is to get the API key from their website. TMDB is a popular website for getting information on movies, it’s a community built movie and TV database. It also provides a REST API that is well documented. Queries can be built using this API. click here to see the API documentation.

Click here for the website’s instructions on how to obtain API key, you just need to register and be logged in.

CREATE ANDROID APP

  1. Create new project in Android Studio from File, New Project. When it prompts you to select the default activity, select Empty Activity and proceed. Please note that the sample code for this app is on my GitHub Repo, so make sure you check it out for the complete source code and other resources for the project.
dependencies {compile fileTree(dir: ‘libs’, include: [‘*.jar’])testCompile ‘junit:junit:4.12’compile ‘com.android.support:appcompat-v7:23.3.0’// retrofit, gsoncompile ‘com.squareup.picasso:picasso:2.5.2’compile ‘com.squareup.retrofit2:retrofit:2.1.0’compile ‘com.squareup.retrofit2:converter-gson:2.1.0’compile ‘com.android.support:recyclerview-v7:25.0.0’}

You might be wondering why add recyclerview and picasso dependencies, since we’ll be using Picasso to load image URLs into an imageView and we’ll display the movie details in a RecyclerView, so adding their dependencies at this stage is wise.

3. Since we’ll be querying the TMDB API which is a network operations we’ll need to add INTERNET permissions in AndroidManifest.xml file:

<?xml version=”1.0" encoding=”utf-8"?><manifest xmlns:android=”http://schemas.android.com/apk/res/android"package=”app.movie.tutorial.com”><uses-permission android:name=”android.permission.INTERNET”/><applicationandroid:icon=”@mipmap/ic_launcher”android:label=”@string/app_name”android:theme=”@style/AppTheme”><activity android:name=”app.movie.tutorial.com.activity.MainActivity”><intent-filter><action android:name=”android.intent.action.MAIN” /><category android:name=”android.intent.category.LAUNCHER” /></intent-filter></activity></application></manifest>

4. Create four sub packages named activity, adapter, rest and model in your main package. Move your empty MainActivity into the activity package.

Remember that after querying TMDB API, a JSON response will be returned. We need to know the exact fields that will be included in the response, so we can create a POJO that will be able to parse the latest movies. For us to know these fields in advance, let’s first define how a basic movie representation should look like

Using this URL (http://api.themoviedb.org/3/movie/top_rated?api_key=INSERT_YOUR_API_KEY) we can get the last 50 movies. Let’s insert it into browser and see. Using JsonViewer you can see JSON in more structured way. Copy the response from browser to this JsonViewer and this online tool show JSON like below:

5. So now that what we have seen the expected fields from the above image which shows the different fields of the JSON response, let’s create a class named Movie.java under model package which is a simple POJO to hold all fields and provide getter and setter methods for fields that we are expecting in the JSON response object. Past the below code in the Movie.java file.

/*** Created by Gino Osahon on 13/03/2017.*/public class Movie {@SerializedName("poster_path")private String posterPath;@SerializedName("adult")private boolean adult;@SerializedName("overview")private String overview;@SerializedName("release_date")private String releaseDate;@SerializedName("genre_ids")private List<Integer> genreIds = new ArrayList<Integer>();@SerializedName("id")private Integer id;@SerializedName("original_title")private String originalTitle;@SerializedName("original_language")private String originalLanguage;@SerializedName("title")private String title;@SerializedName("backdrop_path")private String backdropPath;@SerializedName("popularity")private Double popularity;@SerializedName("vote_count")private Integer voteCount;@SerializedName("video")private Boolean video;@SerializedName("vote_average")private Double voteAverage;public Movie(String posterPath, boolean adult, String overview, String releaseDate, List<Integer> genreIds, Integer id,String originalTitle, String originalLanguage, String title, String backdropPath, Double popularity,Integer voteCount, Boolean video, Double voteAverage) {this.posterPath = posterPath;this.adult = adult;this.overview = overview;this.releaseDate = releaseDate;this.genreIds = genreIds;this.id = id;this.originalTitle = originalTitle;this.originalLanguage = originalLanguage;this.title = title;this.backdropPath = backdropPath;this.popularity = popularity;this.voteCount = voteCount;this.video = video;this.voteAverage = voteAverage;}public String getPosterPath() {return posterPath;}public void setPosterPath(String posterPath) {this.posterPath = posterPath;}public boolean isAdult() {return adult;}public void setAdult(boolean adult) {this.adult = adult;}public String getOverview() {return overview;}public void setOverview(String overview) {this.overview = overview;}public String getReleaseDate() {return releaseDate;}public void setReleaseDate(String releaseDate) {this.releaseDate = releaseDate;}public List<Integer> getGenreIds() {return genreIds;}public void setGenreIds(List<Integer> genreIds) {this.genreIds = genreIds;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getOriginalTitle() {return originalTitle;}public void setOriginalTitle(String originalTitle) {this.originalTitle = originalTitle;}public String getOriginalLanguage() {return originalLanguage;}public void setOriginalLanguage(String originalLanguage) {this.originalLanguage = originalLanguage;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getBackdropPath() {return backdropPath;}public void setBackdropPath(String backdropPath) {this.backdropPath = backdropPath;}public Double getPopularity() {return popularity;}public void setPopularity(Double popularity) {this.popularity = popularity;}public Integer getVoteCount() {return voteCount;}public void setVoteCount(Integer voteCount) {this.voteCount = voteCount;}public Boolean getVideo() {return video;}public void setVideo(Boolean video) {this.video = video;}public Double getVoteAverage() {return voteAverage;}public void setVoteAverage(Double voteAverage) {this.voteAverage = voteAverage;}}

6. We also need to create MovieResponse.java class inside the model package, since we have some extra fields like page number. This class contains all fetched movies and extra information. Create MovieResponse.java under model package and past the below code.

/*** Created by Gino Osahon on 13/03/2017.*/// This class contains all fetched movies and extra informationpublic class MovieResponse {@SerializedName("page")private int page;@SerializedName("results")private List<Movie> results;@SerializedName("total_results")private int totalResults;@SerializedName("total_pages")private int totalPages;public int getPage() {return page;}public void setPage(int page) {this.page = page;}public List<Movie> getResults() {return results;}public void setResults(List<Movie> results) {this.results = results;}public int getTotalResults() {return totalResults;}public void setTotalResults(int totalResults) {this.totalResults = totalResults;}public int getTotalPages() {return totalPages;}public void setTotalPages(int totalPages) {this.totalPages = totalPages;}}

7. Next, let’s define the Interface containing the Endpoint method used to query the TMDB API. Special retrofit annotations are used to encode details about the parameters and request method. the interface defines each endpoint in the following way. Note that we only have one endpoint method in the Interface, in a real programming world scenario, you can have multiple endpoints methods inside the interface. So create an interface named MovieApiService.java and past the below codes.

/*** Created by Gino Osahon on 13/03/2017.*/public interface MovieApiService {@GET(“movie/top_rated”)Call<MovieResponse> getTopRatedMovies(@Query(“api_key”) String apiKey);}

8. Let’s now create the MainActivty.java class which is the activity from where we make request to the Movie DB API. Open the MainActivity.java and insert in the below codes.

public class MainActivity extends AppCompatActivity{private static final String TAG = MainActivity.class.getSimpleName();public static final String BASE_URL = "http://api.themoviedb.org/3/";private static Retrofit retrofit = null;private RecyclerView recyclerView = null;// insert your themoviedb.org API KEY hereprivate final static String API_KEY = "";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);recyclerView = (RecyclerView) findViewById(R.id.recycler_view);recyclerView.setHasFixedSize(true);recyclerView.setLayoutManager(new LinearLayoutManager(this));connectAndGetApiData();}// This method create an instance of Retrofit// set the base urlpublic void connectAndGetApiData(){if (retrofit == null) {retrofit = new Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build();}MovieApiService movieApiService = retrofit.create(MovieApiService.class);Call<MovieResponse> call = movieApiService.getTopRatedMovies(API_KEY);call.enqueue(new Callback<MovieResponse>() {@Overridepublic void onResponse(Call<MovieResponse> call, Response<MovieResponse> response) {List<Movie> movies = response.body().getResults();recyclerView.setAdapter(new MoviesAdapter(movies, R.layout.list_item_movie, getApplicationContext()));Log.d(TAG, "Number of movies received: " + movies.size());}@Overridepublic void onFailure(Call<MovieResponse> call, Throwable throwable) {Log.e(TAG, throwable.toString());}});}}

Retrofit will download and parse the API data on a background thread, and then return the results back to the UI thread via the onResponse or onFailure method.

9. It’s time to display the result gotten from querying the API using the above codes on the layout. We’ll need to create a layout which will contain 4 textViews and 2 imageViews.

Open colors.xml and add the below color values<?xml version="1.0" encoding="utf-8"?><resources><color name="colorPrimary">#3F51B5</color><color name="colorPrimaryDark">#303F9F</color><color name="colorAccent">#FF4081</color><color name="orange">#FF3909</color><color name="colorAccentDark">#00B482</color><color name="colorBlack">#555555</color><color name="colorWhite">#FFFFFF</color><color name="colorGrey">#707070</color><color name="colorGreyLight">#8A8A8A</color></resources>

10. In other to the display the ratings, we need to create the star imageView, so create a layout named star.xml under res drawable with the below content.

<?xml version=”1.0" encoding=”utf-8"?><! — drawable/star.xml →<vector xmlns:android=”http://schemas.android.com/apk/res/android"android:width=”24dp”android:height=”24dp”android:viewportHeight=”24"android:viewportWidth=”24"><pathandroid:fillColor=”#000"android:pathData=”M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z” /></vector>

11. Open the activity_main.xml file and copy the below code into it.

<?xml version=”1.0" encoding=”utf-8"?><LinearLayout 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”tools:context=”app.movie.tutorial.com.activity.MainActivity”><android.support.v7.widget.RecyclerViewandroid:id=”@+id/recycler_view”android:scrollbars=”vertical”android:layout_width=”match_parent”android:layout_height=”match_parent”/></LinearLayout>

12. Create a layout named list_item_movie.xml under res layout folder

<?xml version=”1.0" encoding=”utf-8"?><LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android"android:id=”@+id/movies_layout”android:layout_width=”match_parent”android:layout_height=”wrap_content”android:gravity=”center_vertical”android:minHeight=”72dp”android:orientation=”horizontal”android:padding=”16dp”><LinearLayoutandroid:layout_width=”0dp”android:layout_height=”wrap_content”android:layout_weight=”1"android:orientation=”vertical”><ImageViewandroid:id=”@+id/movie_image”android:layout_width=”wrap_content”android:layout_height=”wrap_content”android:layout_gravity=”top”android:paddingRight=”16dp”/><TextViewandroid:id=”@+id/title”android:layout_width=”wrap_content”android:layout_height=”wrap_content”android:paddingRight=”16dp”android:textStyle=”bold”android:textColor=”@color/colorBlack”android:textSize=”16sp” /><TextViewandroid:id=”@+id/date”android:layout_width=”wrap_content”android:layout_height=”wrap_content”android:paddingRight=”16dp”android:textColor=”@color/colorGreyLight” /><TextViewandroid:id=”@+id/description”android:layout_width=”wrap_content”android:layout_height=”wrap_content”android:maxLines=”3"android:paddingRight=”16dp”android:textColor=”@color/colorGreyLight” /></LinearLayout><LinearLayoutandroid:layout_width=”wrap_content”android:layout_height=”35dp”android:orientation=”horizontal”><ImageViewandroid:id=”@+id/rating_image”android:layout_width=”15dp”android:layout_height=”15dp”android:layout_centerInParent=”true”android:scaleType=”centerCrop”android:src=”@drawable/star”android:tint=”@color/colorAccent” /><TextViewandroid:id=”@+id/rating”android:layout_width=”wrap_content”android:layout_height=”wrap_content”android:layout_marginLeft=”8dp”android:text=”5.0" /></LinearLayout></LinearLayout>

12. Adapter is a common pattern which helps to bind view and data, so let’s implement adapter for this. Create a class named MoviesAdapter.java under adapter package.

/*** Created by Gino Osahon on 14/03/2017.*/public class MoviesAdapter extends RecyclerView.Adapter<MoviesAdapter.MovieViewHolder> {private List<Movie> movies;private int rowLayout;private Context context;public static final String IMAGE_URL_BASE_PATH=”http://image.tmdb.org/t/p/w342//";public MoviesAdapter(List<Movie> movies, int rowLayout, Context context) {this.movies = movies;this.rowLayout = rowLayout;this.context = context;}//A view holder inner class where we get reference to the views in the layout using their IDpublic static class MovieViewHolder extends RecyclerView.ViewHolder {LinearLayout moviesLayout;TextView movieTitle;TextView data;TextView movieDescription;TextView rating;ImageView movieImage;public MovieViewHolder(View v) {super(v);moviesLayout = (LinearLayout) v.findViewById(R.id.movies_layout);movieImage = (ImageView) v.findViewById(R.id.movie_image);movieTitle = (TextView) v.findViewById(R.id.title);data = (TextView) v.findViewById(R.id.date);movieDescription = (TextView) v.findViewById(R.id.description);rating = (TextView) v.findViewById(R.id.rating);}}@Overridepublic MoviesAdapter.MovieViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(rowLayout, parent, false);return new MovieViewHolder(view);}@Overridepublic void onBindViewHolder(MovieViewHolder holder, final int position) {String image_url = IMAGE_URL_BASE_PATH + movies.get(position).getPosterPath();Picasso.with(context).load(image_url).placeholder(android.R.drawable.sym_def_app_icon).error(android.R.drawable.sym_def_app_icon).into(holder.movieImage);holder.movieTitle.setText(movies.get(position).getTitle());holder.data.setText(movies.get(position).getReleaseDate());holder.movieDescription.setText(movies.get(position).getOverview());holder.rating.setText(movies.get(position).getVoteAverage().toString());}@Overridepublic int getItemCount() {return movies.size();}}

Now save and run the App. You can download the full source code for this project on GitHub