Consume an API with Retrofit in Android

Consume an API with Retrofit in Android

Here in this article I am demonstrating how you can consume an API using Retrofit in Android. For this specific tutorial we will be using the GET HTTP verb to fetch comic detail from the Xkcd Comics.

Contents

What is Retrofit ?
Adding the required dependencies
Preparing our UI
Setting up Retrofit
    – Preparing JSON response POKO
    – Creating Xkcd service
    – Preparing Retrofit for use
    – Creating the ViewModel
    – Using the ViewModel

What is Retrofit ?

According to them Retrofit is a “A type-safe HTTP client for Android and Java“. Now what do they mean by type-safe and HTTP client ? Let me do some break down.

In a simple words type-safe in computer science means that there is no data and data-type mismatch while assigning data to a memory address. It ensures that the data is always correctly referenced. And there are no unexpected errors .

And that brings us to HTTP client. What is a HTTP client ?HTTP client is simply the applications that communicate with HTTP server. Simple enough, yes. Lets dive right into the heart of this article.

Also Read : Learn Android Snackbar the Material Design Way

TECHENUM

Adding the required dependencies

So, now that we have understood what Retrofit really is. We must look at what dependencies we will be using to perform a simple network request using retrofit. The libraries marked with * actually has nothing to do with consuming API.

Things we will be needing:

  • Retrofit library
  • Gson library
  • Lifecycle extensions ( for using view model )
  • Coil library ( for image ) *

Let us add the all libraries in our app > build.gradle file

dependencies { // retrofit implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // for view model implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' // for image loading implementation "io.coil-kt:coil:0.13.0" }

Do a gradle sync and we have successfully setup our libraries. One step closer to consume API with Retrofit on Android.

Also Read : Learn How to Use Custom Font in Android Application

TECHENUM

Preparing our UI

This step is optional and if you are not following along to every bit of code this is pretty useless. You can skip to the next section. But stay if you are only starting to learn Android.

In my activity_main.xml I have the following code:

<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/text" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginTop="16dp" android:layout_marginRight="16dp" android:layout_marginBottom="16dp" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="1.0" /> <ImageView android:id="@+id/imageView" android:layout_width="0dp" android:layout_height="0dp" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:layout_marginBottom="16dp" app:layout_constraintBottom_toTopOf="@+id/text" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:srcCompat="@tools:sample/avatars" /> </androidx.constraintlayout.widget.ConstraintLayout>

And then I have mapped the views to the MainActivity class as below

class MainActivity : AppCompatActivity() { private lateinit var mText: TextView private lateinit var mImageView: ImageView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) mText = findViewById(R.id.text) mImageView = findViewById(R.id.imageView) } }

Now that we have setup our MainActivity let us move forward.

Also Read : Learn how to use shared preferences in Android

TECHENUM

Setting up Retrofit

Now that we have setup our UI we have to set everything up for us to be able to use the Retrofit for consuming Xkcd API.

We will need the following things

  • Service ( the API endpoints )
  • Concrete implementation of the service

Now that we have understood this. Let us move forward and see how we prepare each one of them.

Preparing JSON response POKO

First visit this URL, click here. We have to convert the response into the POKO ( Plain Old Kotlin Object ).

data class ComicResponse( @SerializedName("alt") var alt: String, @SerializedName("day") var day: String, @SerializedName("img") var img: String, @SerializedName("link") var link: String, @SerializedName("month") var month: String, @SerializedName("news") var news: String, @SerializedName("num") var num: Int, @SerializedName("safe_title") var safeTitle: String, @SerializedName("title") var title: String, @SerializedName("transcript") var transcript: String, @SerializedName("year") var year: String )

We have the annotation @SerializedName which maps the JSON key to the variable in the class.

Once we are done mapping JSON to actual variable.

Also Read : Learn How To Create Tabs With ViewPager2

TECHENUM

Creating Xkcd service

Create a Kotlin interface with the name XkcdApiService. Yes, this is not a class but an interface.

interface XkcdApiService { @GET("info.0.json") fun getFrontPageComic(): Call<ComicResponse> }

For now copy the code above and paste it into your file.

There is a few things we have to understand about the above XkcdApiService interface.

  1. @GET is an HTTP verb annotation, we will cover about annotations later
  2. The annotation will have some value inside the parentheses
  3. The return type Call<> is a non blocking method which has onSuccess { } and onError() { }. This is where we get our response from server.

Why is an service interface required ?

To learn more about interface you can read: Interface in OOP: Guide to Polymorphism and Callbacks.

Here our library Retrofit takes the interface and created suitable implementation for us to use during the runtime. Meaning it will write the boilerplate code for us.

Also Read : How to get current GPS location in Android

TECHENUM

Preparing Retrofit for use

Now that we have created a POKO and service. It is time for us to create a retrofit implementation which we will be using as a singleton.

let us create an object named RetrofitClient as below:

object RetrofitClient { private lateinit var INSTANCE: XkcdApiService private const val MS_TIMEOUT = 30L private const val BASE_URL = "https://xkcd.com/" fun instance(): XkcdApiService { if (!::INSTANCE.isInitialized) { val client = OkHttpClient.Builder().apply { /*callTimeout(MS_TIMEOUT, TimeUnit.SECONDS) connectTimeout(MS_TIMEOUT, TimeUnit.SECONDS) readTimeout(MS_TIMEOUT, TimeUnit.SECONDS) writeTimeout(MS_TIMEOUT, TimeUnit.SECONDS)*/ }.build() val retrofit = Retrofit.Builder().apply { baseUrl(BASE_URL) client(client) addConverterFactory(GsonConverterFactory.create()) }.build() INSTANCE = retrofit.create(XkcdApiService::class.java) } return INSTANCE } }

Let me quickly explain what the class does. We have a variable named INSTANCE where we will be holding the instance of the XkcdApiService.

We have a OkHttpClient.Builder which looks after all the network related things such as read / write and call timeout.

Next we have Retrofit.Builder which actually takes in our URL and GsonFactory. We have to watch out for 2 main things here.

Base URL : It is nothing but the static part of the API URL which does not change. In our case https://xkcd.com/. It does not change we will be calling a resource in that domain.

Converter Factory : This portion tells the retrofit how to convert the response to object in Kotlin / Java. We have used Gson so we will be using GsonConverterFactory.

We have finally created an instance and assigned it with the following line

INSTANCE = retrofit.create(XkcdApiService::class.java)

Also Read : Async Task in Android is Deprecated: There are Better Ways

TECHENUM

Creating the ViewModel

We are following the MVVM pattern so let us quickly create a ViewModel first.

We have to create a ViewModel named MainActivityViewModel as below :

class MainActivityViewModel : ViewModel() { private val mXkcdApiService = RetrofitClient.instance() fun getComic(): MutableLiveData<ComicResponse?> { val result = MutableLiveData<ComicResponse?>() mXkcdApiService.getFrontPageComic().enqueue(object : Callback<ComicResponse> { override fun onResponse(call: Call<ComicResponse>, response: Response<ComicResponse>) { if (!response.isSuccessful) { result.postValue(null) } else { result.postValue(response.body()) } } override fun onFailure(call: Call<ComicResponse>, t: Throwable) { result.postValue(null) } }) return result } }

Remember the method we created earlier in XkcdApiService ?

@GET("info.0.json") fun getFrontPageComic(): Call<ComicResponse>

The Call<T> has a method named enqueue() which will perform the network request and passes the result in form of Callback<T>.

And that is exactly what we have passed in the ViewModel‘s invocation of enqueue(). Once the request is complete we will receive the response in either onResponse() or onFailure().

Let me briefly go over what each of the method does.

When the request completes the code inside onResponse() is executed. The response is complete but we don’t know whether the request succeeded or failed.

So to see if the request succeeded we must invoke response.isSuccessful(). This will be true if HTTP response code is between 200 and 299.

Also Read : ZOOM SDK Android: Learn How to Create an Online Meeting

TECHENUM

Using the ViewModel

Now that we have our ViewModel ready let us update the MainActivity code add the code below at the end of onCreate() { }

val vm = ViewModelProvider(this).get(MainActivityViewModel::class.java) vm.getComic().observe(this, Observer { if (it == null) { Log.i("MainActivity", "we received null, there was an error perform inspection.") } else { Log.i("MainActivity", "we have alt : ${it.alt}") mText.text = it.alt mImageView.load(it.img) } })

We have nothing going on here we have just checked if the value of live data is null. If the value is not null we have updated our TextView and ImageView.

Look at the mImageView.load(it.img) the method .load() is an extension method provided by the Coil library which we had used earlier. You could have simply used the Glide or Picasso.

The final MainActivity file will look something like this :

class MainActivity : AppCompatActivity() { private lateinit var mText: TextView private lateinit var mImageView: ImageView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) mText = findViewById(R.id.text) mImageView = findViewById(R.id.imageView) val vm = ViewModelProvider(this).get(MainActivityViewModel::class.java) vm.getComic().observe(this, Observer { if (it == null) { Log.i("MainActivity", "we received null, there was an error perform inspection.") } else { Log.i("MainActivity", "we have alt : ${it.alt}") mText.text = it.alt mImageView.load(it.img) } }) } }

That is it. You have learned how to consume API with retrofit in android. And I hope it was clear enough for everyone. Feel free to leave a comment if you are confused about anything at all.

I have purposely not used coroutines, I will provide a separate article on how you can use coroutine to do exactly this soon.

Until then: Keep learning.

Naveen Niraula

An Android developer with passion for latest technologies. Likes to learn and share the findings and techniques. Also shows keen interest in networking.

Leave a Reply