DEV Community

loading...
Cover image for Making GET Requests with Retrofit2 on Android using Kotlin

Making GET Requests with Retrofit2 on Android using Kotlin

Aayush Gupta
A 22 y.o. Android Developer from Bhilai who also happens to be an avid FOSS Contributor
・5 min read

Retrofit is the most commonly used, type-safe HTTP library for Android. We can use it to work with REST API provided by various websites and servers. In this article, I will show you how to make simple GET requests using Retrofit.

Target Server

We will use JSONPlaceholder which is a free service to make REST API requests. To be specific, we will target:

  • /posts/1 which provides a single JSON object in response
  • /posts/ which provides an array of 100 JSON objects in response

Dependencies

Add a dependency on Retrofit, Coroutines, and Lifecycle libraries of the android.

dependencies {
    // Retrofit
    def retrofit_version = "2.9.0"
    implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
    implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"

    // Coroutines
    def coroutines_version = "1.4.2"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"

    // Lifecycle
    def lifecycle_version = "2.2.0"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
}
Enter fullscreen mode Exit fullscreen mode

From the above dependencies, we will be able to use

  • Retrofit to make requests,
  • GSON to convert JSON to Kotlin and vice-versa,
  • Coroutines to make our requests in a separate thread,
  • ViewModel to store our fetched data, and
  • LiveData to observe the stored data

Usage

Permission Declaration

Declare permission in your module's AndroidManifest.xml to be able to use the internet in your app.

<uses-permission android:name="android.permission.INTERNET"/>
Enter fullscreen mode Exit fullscreen mode

Network Implementation

We will store all our classes regarding Retrofit in a new package called network. This package will host 3 files namely:

  • User.kt - Class
  • UserAPI.kt - Interface
  • UserNetwork.kt - Object

Now let's proceed to implement these classes.

User

This class will hold a data class called User which will contain 4 arguments similar to the response object provided by the API.

Here is how User.kt looks in my case:

package dev.theimpulson.retrofitexample.network

data class User(
    val body: String,
    val id: Int,
    val title: String,
    val userId: Int
)
Enter fullscreen mode Exit fullscreen mode

UserAPI

UserAPI will be an interface. This will host our GET methods to actually fetch the JSON response. Declare 2 suspend functions with @GET annotations and their response. These annotations will host the path to make the requests. Making these functions suspend will ensure that they will be only executed using coroutines scope.

Here is how UserAPI.kt looks in my case:

package dev.theimpulson.retrofitexample.network

import retrofit2.http.GET

interface UserAPI {

    @GET("posts/1")
    suspend fun getPost(): User

    @GET("posts")
    suspend fun getPosts(): List<User>
}
Enter fullscreen mode Exit fullscreen mode

UserNetwork

UserNetwork will be an object to make it a singleton. This will host our Retrofit's Builder method to initialize Retrofit.

Declare a variable to be lazily initialized. This will initialize Retrofit.Builder() class. We will call:

  • .baseUrl("https://jsonplaceholder.typicode.com") method to declare the baseurl,
  • .addConverterFactory(GsonConverterFactory.create()) to declare the converter which will convert JSON to Kotlin and vice-versa,
  • .build() to build the object, and
  • .create(UserAPI::class.java) to pass our interface methods to it.

Here is how UserNetwork.kt looks in my case:

package dev.theimpulson.retrofitexample.network

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

object UserNetwork {

    val retrofit by lazy {
        Retrofit.Builder()
                .baseUrl("https://jsonplaceholder.typicode.com")
                .addConverterFactory(GsonConverterFactory.create())
                .build()
                .create(UserAPI::class.java)
    }
}
Enter fullscreen mode Exit fullscreen mode

and with this our network package is complete. We can now use it to make GET requests to obtain the JSON response from the target server.

ViewModel Implementation

Create a new class that will extend to the ViewModel library. This class will hold 2 variables to store the fetched responses and 2 functions to invoke the suspend functions in the viewModelScope respectively.

The variables will be val, have a type of MutableLiveData regarding the User, and will initialize MutableLiveData class.

The functions will launch our UserAPI's suspend functions in viewModelScope to update the value of the variables we created above with the response provided by the server.

I will name it as MainActivityViewModel in my case. Here is how it looks like:

package dev.theimpulson.retrofitexample

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dev.theimpulson.retrofitexample.network.User
import dev.theimpulson.retrofitexample.network.UserNetwork
import kotlinx.coroutines.launch

class MainActivityViewModel : ViewModel() {

    val myResponse: MutableLiveData<User> = MutableLiveData()
    val myResponseList: MutableLiveData<List<User>> = MutableLiveData()

    fun getPost() {
        viewModelScope.launch {
            myResponse.value = UserNetwork.retrofit.getPost()
        }
    }

    fun getPosts() {
        viewModelScope.launch {
            myResponseList.value = UserNetwork.retrofit.getPosts()
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

MainActivity Implementation

Now in our MainActivity, we will get an instance of the ViewModel class we created earlier using ViewModelProvider class.

val viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
Enter fullscreen mode Exit fullscreen mode

Now, we will invoke the 2 functions in ViewModel created earlier to fetch and store the data into the class variable and use LiveData's observe method on them to Log it.

  • Invoking the first function for the single JSON object and logging its response for observing.
viewModel.getPost()
viewModel.myResponse.observe(this, Observer {
    Log.d(TAG, it.body)
    Log.d(TAG, it.title)
    Log.d(TAG, it.id.toString())
    Log.d(TAG, it.userId.toString())
})
Enter fullscreen mode Exit fullscreen mode
  • Invoking the second function for the List of 100 JSON objects and logging its response for observing. Since the response is a list of User objects, we can loop over them to Log every single one of them one-by-one.
viewModel.getPosts()
viewModel.myResponseList.observe(this, Observer {
    for (user in it) {
        Log.d(TAG, user.body)
        Log.d(TAG, user.title)
        Log.d(TAG, user.id.toString())
        Log.d(TAG, user.userId.toString())
}
Enter fullscreen mode Exit fullscreen mode

Here is how MainActivity.kt looks in my case:

package dev.theimpulson.retrofitexample

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import dev.theimpulson.retrofitexample.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private val TAG = "MainActivity"
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)

        viewModel.getPost()
        viewModel.myResponse.observe(this, Observer {
            Log.d(TAG, it.body)
            Log.d(TAG, it.title)
            Log.d(TAG, it.id.toString())
            Log.d(TAG, it.userId.toString())
        })

        viewModel.getPosts()
        viewModel.myResponseList.observe(this, Observer {
            for (user in it) {
                Log.d(TAG, user.body)
                Log.d(TAG, user.title)
                Log.d(TAG, user.id.toString())
                Log.d(TAG, user.userId.toString())
            }
        })
    }
}
Enter fullscreen mode Exit fullscreen mode

With this, our package is now complete and ready to use. You can build and run it on a device of your choice and observe the logcat to see that the responses are logged as we expected.

You should be able to modify, extend, and use this way to make GET requests with almost any website/server that provides a REST API in your Android app.

Additional Resources

In the above article, I have used ViewModel and ViewBinding. In case you are unaware of them and want a quick overview of they work, checkout my articles on those 2 topics:

Credits:

Discussion (0)