Rest API integration in Android Studio using Retrofit 2

Vishnu Sivan
Jun 13 · 12 min read

A Web API is an application programming interface for either a web server or a web browser. API allows developers to interact with external services. These are the commands that the developer of the service has determined will be used to access certain features of their program.

Most APIs work using either XML or JSON. These languages allow us to send and retrieve large amounts of useful information in the form of objects.

XML is easy to understand and generally places keys inside triangle brackets, followed by their values.

<?xml version="1.0" encoding="UTF-8"?>
<book>
<author>Marijn Haverbeke</author>
<description>JavaScript lies at the heart of almost every modern web application, from social apps like Twitter to browser-based game frameworks like Phaser and Babylon. Though simple for beginners to pick up and play with, JavaScript is a flexible, complex language that you can use to build full-scale applications.</description>
<isbn>978-1593279509</isbn>
<pages>472</pages>
<published>2018-12-04T00:00:00.000Z</published>
<publisher>No Starch Press</publisher>
<subtitle>A Modern Introduction to Programming</subtitle>
<thumbnail>https://retrofit-backend-demo.herokuapp.com/eloquentjavascript.jpg</thumbnail>
<title>Eloquent JavaScript, Third Edition</title>
<website>http://eloquentjavascript.net/</website>
</book>

JSON, “Javascript Object Notation.” It is a short-hand for sending data as value/attribute pairs.

{
"book":{
"isbn":"978-1593279509",
"title":"Eloquent JavaScript, Third Edition",
"subtitle":"A Modern Introduction to Programming",
"author":"Marijn Haverbeke",
"published":"2018-12-04T00:00:00.000Z",
"publisher":"No Starch Press",
"pages":472,
"description":"JavaScript lies at the heart of almost every modern web application, from social apps like Twitter to browser-based game frameworks like Phaser and Babylon. Though simple for beginners to pick up and play with, JavaScript is a flexible, complex language that you can use to build full-scale applications.",
"website":"http://eloquentjavascript.net/",
"thumbnail":"https://retrofit-backend-demo.herokuapp.com/eloquentjavascript.jpg"
}
}

REST API

A REST API is an application programming interface (API) that conforms to the constraints of REST architectural style and allows for interaction with RESTful web services. REST stands for representational state transfer and was created by computer scientist Roy Fielding.

REST API is designed to take advantage of existing protocols. While REST can be used over nearly any protocol, it usually takes advantage of HTTP when used for Web APIs. This means that developers do not need to install libraries or additional software in order to take advantage of a REST API design.

Retrofit

Retrofit is a type-safe REST client for Android, Java and Kotlin developed by Square. The library provides a powerful framework for authenticating and interacting with APIs and sending network requests with OkHttp.

This library makes downloading JSON or XML data from a web API fairly straightforward. Once the data is downloaded then it is parsed into a Plain Old Java Object (POJO) which must be defined for each “resource” in the response.

Today we are going to see how to use Retrofit HTTP client in your Android application.

Getting Started

This tutorial contains two parts

  • Basic REST API creation in nodejs using express and hosting it in heroku.
  • Basic android app creation and consume the custom api in it.

1. REST API creation

PART 1

Consider I am going to create a REST API for reading some book information. For that, we have a JSON based database of books having the following books in a file books.json:

{
"books":[
{
"isbn":"978-1593279509",
"title":"Eloquent JavaScript, Third Edition",
"subtitle":"A Modern Introduction to Programming",
"author":"Marijn Haverbeke",
"published":"2018-12-04T00:00:00.000Z",
"publisher":"No Starch Press",
"pages":472,
"description":"JavaScript lies at the heart of almost every modern web application, from social apps like Twitter to browser-based game frameworks like Phaser and Babylon. Though simple for beginners to pick up and play with, JavaScript is a flexible, complex language that you can use to build full-scale applications.",
"website":"http://eloquentjavascript.net/",
"thumbnail": "https://retrofit-backend-demo.herokuapp.com/eloquentjavascript.jpg"
},
{
"isbn":"978-1491943533",
"title":"Practical Modern JavaScript",
"subtitle":"Dive into ES6 and the Future of JavaScript",
"author":"Nicolás Bevacqua",
"published":"2017-07-16T00:00:00.000Z",
"publisher":"O'Reilly Media",
"pages":334,
"description":"To get the most out of modern JavaScript, you need learn the latest features of its parent specification, ECMAScript 6 (ES6). This book provides a highly practical look at ES6, without getting lost in the specification or its implementation details.",
"website":"https://github.com/mjavascript/practical-modern-javascript",
"thumbnail": "https://retrofit-backend-demo.herokuapp.com/practical-modern-javascript.png"
},
{
"isbn":"978-1593277574",
"title":"Understanding ECMAScript 6",
"subtitle":"The Definitive Guide for JavaScript Developers",
"author":"Nicholas C. Zakas",
"published":"2016-09-03T00:00:00.000Z",
"publisher":"No Starch Press",
"pages":352,
"description":"ECMAScript 6 represents the biggest update to the core of JavaScript in the history of the language. In Understanding ECMAScript 6, expert developer Nicholas C. Zakas provides a complete guide to the object types, syntax, and other exciting changes that ECMAScript 6 brings to JavaScript.",
"website":"https://leanpub.com/understandinges6/read",
"thumbnail": "https://retrofit-backend-demo.herokuapp.com/understandinges6.png"
},
{
"isbn":"978-1449365035",
"title":"Speaking JavaScript",
"subtitle":"An In-Depth Guide for Programmers",
"author":"Axel Rauschmayer",
"published":"2014-04-08T00:00:00.000Z",
"publisher":"O'Reilly Media",
"pages":460,
"description":"Like it or not, JavaScript is everywhere these days -from browser to server to mobile- and now you, too, need to learn the language or dive deeper than you have. This concise book guides you into and through JavaScript, written by a veteran programmer who once found himself in the same position.",
"website":"http://speakingjs.com/",
"thumbnail": "https://retrofit-backend-demo.herokuapp.com/speakingjs.jpg"
},
{
"isbn":"978-1449331818",
"title":"Learning JavaScript Design Patterns",
"subtitle":"A JavaScript and jQuery Developer's Guide",
"author":"Addy Osmani",
"published":"2012-08-30T00:00:00.000Z",
"publisher":"O'Reilly Media",
"pages":254,
"description":"With Learning JavaScript Design Patterns, you'll learn how to write beautiful, structured, and maintainable JavaScript by applying classical and modern design patterns to the language. If you want to keep your code efficient, more manageable, and up-to-date with the latest best practices, this book is for you.",
"website":"http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/",
"thumbnail": "https://retrofit-backend-demo.herokuapp.com/essentialjsdesignpatterns.jpg"
},
{
"isbn":"979-8602477429",
"title":"You Don't Know JS Yet",
"subtitle":"Get Started",
"author":"Kyle Simpson",
"published":"2020-01-28T00:00:00.000Z",
"publisher":"Independently published",
"pages":143,
"description":"The worldwide best selling You Don't Know JS book series is back for a 2nd edition: You Don't Know JS Yet. All 6 books are brand new, rewritten to cover all sides of JS for 2020 and beyond.",
"website":"https://github.com/getify/You-Dont-Know-JS/tree/2nd-ed/get-started",
"thumbnail": "https://retrofit-backend-demo.herokuapp.com/you-dont-know-js.png"
}
]
}

First we have to create a db handler to read the content from the books.json file. (db_handler/book.js)

const fs = require("fs");const getBooks = () => {
return new Promise(function(resolve, reject) {
fs.readFile("./books.json", "utf8", (err, res) => {
if (err) {
console.log("Error reading file from disk:", err);
reject(err);
}
try {
resolve(JSON.parse(res).books);
} catch (err) {
console.log("Error parsing JSON string:", err);
reject(err);
}
});
})
}
module.exports = {
getBooks
}

Then create a router file to catch all the requests for books. (routes/book.js)

var express = require('express')
var router = express.Router()
const book = require('../db_handlers/book')
router.get('/', (req, res) => {
book.getBooks()
.then(response => {
res.status(200).send(response);
})
.catch(error => {
res.status(500).send(error);
})
})
module.exports = router

Finally we are going to create a server and add the router in it. (index.js)

require('dotenv').config()const express = require('express')
const app = express()
const port = process.env.PORT || 3001
var logger = require('morgan');
const bookRouter = require('./routes/book')app.use(express.json())
app.use(logger('dev'));
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Access-Control-Allow-Headers');
next();
});
app.use(express.static('public'))app.get('/', (req, res) => {
res.status(200).send("Retrofit Demo Server");
})
app.use('/book', bookRouter)
app.listen(port, () => {
console.log(`App running on port ${port}.`)
})

There is also a public folder for serving images to the client. You can provide access to the directory by the following code in your index file.

app.use(express.static('public'))

You can get the images in your client by it’s names along with server IP.

https://retrofit-backend-demo.herokuapp.com/eloquentjavascript.jpg

Now the server creation part is over. You can run the server by

npm start

PART 2

It’s the time to deploy your node app in heroku. Heroku is a platform as a service (PaaS) that enables developers to build, run, and operate applications entirely in the cloud.

First you have to create an account in heroku and login to the portal. You will get the dashboard like this.

Fig 1. Heroku dashboard

Click on Create new app button.

Fig 2. Create app

Provide an app name and click on Create app. Here I used retrofit-backend-demo. You can use your own name for your app. Now your app is ready and we can deploy the server in the app.

Fig 3. Deploy tab

There are 3 methods available for deployment. I already have installed the Heroku Git in my system and I feel it as the simplest way to deploy your app. So, In this example we are going to push our node app using Heroku Git.

Use the commands that mentioned in the deploy tab (Fig 3) to deploy your app.

Fig 4. Deployment info

After successful deployment you will get a url along with deployed to Heroku message. This will be the url of your server. You can test the url in your browser to verify your app response. If it’s showing any error then use heroku logs to check the logs.

Fig 5. Node app

2. Android app creation

lets create a new project in Android Studio.

  1. Go to File ⇒ New Project. When it prompts you to select the default activity, select Empty Activity and proceed.
  2. First up, we need to add internet permission to our Android Manifest file to make sure our app is allowed to go online.
<uses-permission android:name="android.permission.INTERNET" />

3. Open build.gradle in (Module:app) and add Retrofit, Picasso, RecyclerView, Gson dependencies.

implementation 'com.android.support:recyclerview-v7:26.1.0'
implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'

4. Converting Json to Java object.

A route is a URL that represents an endpoint for the API. If we take a look at JSON Placeholder, you’ll see we have options such as ‘/book’ and ‘/comment’ etc. We already mentioned the JSON representation of books. So, to handle this information, we’re going to need a class that can build objects from the deserialized data.
For that, create a new class in your project named Book under model package. This will need variables that correspond to the data we’re getting from the /book api (isbn, titile, author etc). We’ll be getting that information from the web API, so we need a getter and setter for each of them.

The model class should look like this:

package com.example.demo.retrofitdemo.model;

import com.google.gson.annotations.SerializedName;

/**
* Created by Codemaker on 12/06/2021.
*/

public class Book {
@SerializedName("isbn")
private String isbn;
@SerializedName("title")
private String title;
@SerializedName("subtitle")
private String subtitle;
@SerializedName("author")
private String author;
@SerializedName("published")
private String published;
@SerializedName("publisher")
private String publisher;
@SerializedName("pages")
private Integer pages;
@SerializedName("description")
private String description;
@SerializedName("website")
private String website;
@SerializedName("thumbnail")
private String thumbnail;

public Book() {
}

public Book(String isbn, String title, String subtitle, String author, String published, String publisher, Integer pages, String description, String website, String thumbnail) {
this.isbn = isbn;
this.title = title;
this.subtitle = subtitle;
this.author = author;
this.published = published;
this.publisher = publisher;
this.pages = pages;
this.description = description;
this.website = website;
this.thumbnail = thumbnail;
}

public String getIsbn() {
return isbn;
}

public void setIsbn(String isbn) {
this.isbn = isbn;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getSubtitle() {
return subtitle;
}

public void setSubtitle(String subtitle) {
this.subtitle = subtitle;
}

public String getAuthor() {
return author;
}

public void setAuthor(String author) {
this.author = author;
}

public String getPublished() {
return published;
}

public void setPublished(String published) {
this.published = published;
}

public String getPublisher() {
return publisher;
}

public void setPublisher(String publisher) {
this.publisher = publisher;
}

public Integer getPages() {
return pages;
}

public void setPages(Integer pages) {
this.pages = pages;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public String getWebsite() {
return website;
}

public void setWebsite(String website) {
this.website = website;
}

public String getThumbnail() {
return thumbnail;
}

public void setThumbnail(String thumbnail) {
this.thumbnail = thumbnail;
}
}

5. Create the Retrofit Instance

To issue network requests to a REST API with Retrofit, we need to create an instance using the Retrofit.Builder class and configure it with a base URL.
Create a class RetrofitClientInstance.java under network package. Here BASE_URL is the basic URL of our API where we will make a call.

package com.example.demo.retrofitdemo.network;

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

/**
* Created by Codemaker on 12/06/2021.
*/

public class RetrofitClientInstance {

private static Retrofit retrofit;
private static final String BASE_URL = "https://retrofit-backend-demo.herokuapp.com/";

public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}

6. Define the Endpoints
The endpoints are defined inside of an interface using special retrofit annotations to encode details about the parameters and request method.

This interface needs just a single method to retrieve all the data from /book. If you take a look at that JSON again, you’ll notice that the curly brackets are inside square brackets. This means that we have an array of objects, which is why we want to build a list for them. The objects are instances of our GetDataService that we just made.

package com.example.demo.retrofitdemo.network;

import com.example.demo.retrofitdemo.model.Book;

import java.util.List;

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

/**
* Created by Codemaker on 12/06/2021.
*/

public interface GetDataService {

@GET("/book")
Call<List<Book>> getBooks();
}

7. Create custom adapter for binding data with RecycleView.
Create a class named CustomAdapter.java under adapter package like this.

package com.example.demo.retrofitdemo.adapter;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.text.util.Linkify;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.jakewharton.picasso.OkHttp3Downloader;
import com.squareup.picasso.Picasso;
import com.example.demo.retrofitdemo.R;
import com.example.demo.retrofitdemo.model.Book;

import java.util.List;

/**
* Created by Codemaker on 12/06/2021.
*/

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.CustomViewHolder> {

private List<Book> dataList;
private Context context;

public CustomAdapter(Context context,List<Book> dataList){
this.context = context;
this.dataList = dataList;
}

class CustomViewHolder extends RecyclerView.ViewHolder {

public final View mView;

TextView txtTitle, txtSubtitle, txtAuthor, txtWebsite;
private ImageView coverImage;

CustomViewHolder(View itemView) {
super(itemView);
mView = itemView;

txtTitle = mView.findViewById(R.id.title);
txtSubtitle = mView.findViewById(R.id.subtitle);
txtAuthor = mView.findViewById(R.id.author);
txtWebsite = mView.findViewById(R.id.website);
coverImage = mView.findViewById(R.id.coverImage);
}
}

@Override
public CustomViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(R.layout.custom_row, parent, false);
return new CustomViewHolder(view);
}

@Override
public void onBindViewHolder(CustomViewHolder holder, int position) {
holder.txtTitle.setText(dataList.get(position).getTitle());
holder.txtSubtitle.setText(dataList.get(position).getSubtitle());
holder.txtAuthor.setText(dataList.get(position).getAuthor());
holder.txtWebsite.setText(Html.fromHtml(dataList.get(position).getWebsite()));
Linkify.addLinks(holder.txtWebsite, Linkify.WEB_URLS);
Picasso.Builder builder = new Picasso.Builder(context);
builder.downloader(new OkHttp3Downloader(context));
builder.build().load(dataList.get(position).getThumbnail())
.placeholder((R.drawable.ic_launcher_background))
.error(R.drawable.ic_launcher_background)
.into(holder.coverImage);
}

@Override
public int getItemCount() {
return dataList.size();
}
}

8. Displaying the content
Inside the onCreate() method of the MainActivity.java, we initialize an instance of the GetDataService interface (line 16), the RecyclerView, and also the adapter. Finally, we call the generateDataList() method.

package com.example.demo.retrofitdemo.activity;

import android.app.ProgressDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.widget.Toast;

import com.example.demo.retrofitdemo.R;
import com.example.demo.retrofitdemo.adapter.CustomAdapter;
import com.example.demo.retrofitdemo.model.Book;
import com.example.demo.retrofitdemo.network.GetDataService;
import com.example.demo.retrofitdemo.network.RetrofitClientInstance;

import java.util.List;

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

public class MainActivity extends AppCompatActivity {

private CustomAdapter adapter;
private RecyclerView recyclerView;
ProgressDialog progressDoalog;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

progressDoalog = new ProgressDialog(MainActivity.this);
progressDoalog.setMessage("Loading....");
progressDoalog.show();

/*Create handle for the RetrofitInstance interface*/
GetDataService service = RetrofitClientInstance.getRetrofitInstance().create(GetDataService.class);

Call<List<Book>> call = service.getBooks();
call.enqueue(new Callback<List<Book>>() {

@Override
public void onResponse(Call<List<Book>> call, Response<List<Book>> response) {
progressDoalog.dismiss();
generateDataList(response.body());
}

@Override
public void onFailure(Call<List<Book>> call, Throwable t) {
Log.d("retrofit1", t.toString());
progressDoalog.dismiss();
Toast.makeText(MainActivity.this, "Something went wrong...Please try later!", Toast.LENGTH_SHORT).show();
}
});
}

/*Method to generate List of data using RecyclerView with custom adapter*/
private void generateDataList(List<Book> photoList) {
recyclerView = findViewById(R.id.customRecyclerView);
adapter = new CustomAdapter(this,photoList);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(MainActivity.this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
}
}

Let’s see what’s happening here.

enqueue() asynchronously sends the request and notifies your app with a callback when a response comes back. Since this request is asynchronous, Retrofit handles it on a background thread so that the main UI thread isn't blocked or interfered with.

To use enqueue(), you have to implement two callback methods:

  • onResponse()
  • onFailure()

onFailure is, of course, where we need to handle any errors.

onResponse does not mean that everything went smoothly, however. It simply means that there was a response; that the website exists. Should we get a 404 message, this would still be considered a “response.” Thus, we need to check again if the process went smoothly with isSuccessful(), which checks to see that the HTTP code is not an error.

There you have it! Your own Android App in Android studio :)

I hope you had fun reading and/or following along. In the next story, we will look into how to build more features and interactions into this App. Stay tuned!
If you are interested in further exploring, here are some resources I found helpful along the way:

Nerd For Tech

From Confusion to Clarification

Nerd For Tech

NFT is an Educational Media House. Our mission is to bring the invaluable knowledge and experiences of experts from all over the world to the novice. To know more about us, visit https://www.nerdfortech.org/.

Vishnu Sivan

Written by

Nerd For Tech

NFT is an Educational Media House. Our mission is to bring the invaluable knowledge and experiences of experts from all over the world to the novice. To know more about us, visit https://www.nerdfortech.org/.