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"
}
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"/>
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
)
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>
}
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)
}
}
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()
}
}
}
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)
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())
})
- 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 toLog
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())
}
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())
}
})
}
}
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:
- namespaceit.com for the cover image
Top comments (0)