Learn Retrofit in Android GET with Example

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

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"
}Code language: Groovy (groovy)

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

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>Code language: HTML, XML (xml)

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)

    }

}Code language: Kotlin (kotlin)

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 else. To complete the Retrofit example for consuming Xkcd API in Android.

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
)Code language: Kotlin (kotlin)

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>

}Code language: PHP (php)

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

    }

}Code language: Kotlin (kotlin)

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)Code language: JavaScript (javascript)

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
    }

}Code language: HTML, XML (xml)

Remember the method we created earlier in XkcdApiService ?

@GET("info.0.json") fun getFrontPageComic(): Call<ComicResponse>Code language: HTML, XML (xml)

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)
            }
        })Code language: Kotlin (kotlin)

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)
            }
        })
    }

}Code language: Kotlin (kotlin)

That is it. You have learned with example on how to do GET request 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. But here is a simple coroutine explanation: Kotlin Coroutine in Android : Understanding the Basics

Until then: Keep learning.

Related Posts