Fetching API using Retrofit (Android Jetpack Compose)
Hello, welcome back. For today im going to talk about another android topics which is fetching API using the Retrofit library. Last week I found an interesting free API which is Valorant API. There’re so many endpoints that we can explore on that API. The link of the Valorant API Documentation is https://dash.valorant-api.com/.
Lets move into the dependencies that we might need to use in this project. We can first go into the build.gradle.kts (Module:app)
dependencies {
//ViewModel
implementation ("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
//Retrofit
implementation ("com.squareup.retrofit2:retrofit:2.9.0")
implementation ("com.squareup.retrofit2:converter-gson:2.9.0")
implementation ("com.google.code.gson:gson:2.9.0")
//Coil
implementation("io.coil-kt:coil-compose:2.4.0")
So, in this project we gonna use a viewmodel for the MVVM architecture, Retrofit to help us fetching the API data and also Coil to display an image from link (optional).
Then, we can start by using RoboPOJOGenerator to generate a Data class from API respond. It can be installed on the File > Settings > Plugins > Try to search “RoboPOJOGenerator” and install it. Now we re gonna create a new folder of “data” to hold the data class file. Then, right click on the folder and choose Generate POJO
From there, you can copy the API respond for example to get all agents data from https://valorant-api.com/v1/agents into the RoboPOJOGenerator
Then, choose the language and check on the “use data classes”, “single file” to put all the data class in 1 file and also “nullable fields” to handle a respond data that might be null. For the data class, we can also give the name of it on the “Root object name”. Then the new data class file will be created and look like this
@Parcelize
data class Agents(
@field:SerializedName("data")
val data: List<DataItem>? = null,
@field:SerializedName("status")
val status: Int? = null
) : Parcelable
@Parcelize
data class RecruitmentData(
@field:SerializedName("levelVpCostOverride")
val levelVpCostOverride: Int? = null,
@field:SerializedName("endDate")
val endDate: String? = null,
@field:SerializedName("milestoneThreshold")
val milestoneThreshold: Int? = null,
@field:SerializedName("milestoneId")
val milestoneId: String? = null,
...
Next, we re going to setup our Retrofit. First, we create another folder named “Network” and then we create 2 files which is “RetrofitClient” and also “ApiService”. On the “RetrofitClient”, we create a new Retrofit Instance by first defining the base url of the API which is https://valorant-api.com/v1
object RetrofitInstance {
private const val BASE_URL = "https://valorant-api.com/v1/"
}
And then, we re going to build an instance of the retrofit by calling it using lazy because Retrofit initialization can be a relatively expensive operation, involving the creation of network communication components and setting up various configurations. By using by lazy
, you defer this cost until it's actually necessary.
object RetrofitInstance {
private const val BASE_URL = "https://valorant-api.com/v1/"
private val retrofit: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
}
Next, we gonna move into the ApiService files first to define the endpoint and the http operation. We create an interface of ApiService and then we just need to define what operation needed and also what is the endpoints.
interface ApiService {
@GET("agents")
fun getAllAgents() : Call<Agents>
}
For example, there is a function of getAllAgents() to retrieve all the data agents from the endpoint of “agents” using @GET http protocol . What is endpoint? endpoint is the route of the API. You can simply understand what is baseUrl and endpoint by looking at the picture below
You can create many function to retrieve JSON data from many endpoint and many http protocol that you want.
Now move back into the RetrofitClient. We re going to implement the ApiService that hold the operation into our Retrofit
val apiService: ApiService by lazy {
retrofit.create(ApiService::class.java)
}
So the complete code in the RetrofitClient is look like this
object RetrofitInstance {
private const val BASE_URL = "https://valorant-api.com/v1/"
private val retrofit: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
val apiService: ApiService by lazy {
retrofit.create(ApiService::class.java)
}
}
So, the setup of the retrofit is finished and now you can use it in your ViewModel
class HomeViewModel : ViewModel() {
private val _agentsData : MutableStateFlow<List<DataItem>> = MutableStateFlow(listOf())
val agentsData : StateFlow<List<DataItem>> = _agentsData
private fun retrieveAgentsData(){
viewModelScope.launch {
val call : Call<Agents> = RetrofitInstance.apiService.getAllAgents()
call.enqueue(object : Callback<Agents> {
override fun onResponse(
call: Call<Agents>,
response: Response<Agents>
) {
if(response.isSuccessful){
val responseData: List<DataItem>? = response.body()?.data
if(responseData != null){
_agentsData.value = responseData.filter { dataItem -> dataItem.role?.displayName != null }
}
}
}
override fun onFailure(call: Call<Agents>, t: Throwable) {
Log.d("Failed Retrieve", "Network Error")
}
})
}
}
}
The _agentsData is a private variable that will hold the value of the data and we can change the value of it only in this viewModel. Then, there is agentsData which is a StateFlow that will be observed in the UI and took the data from the _agentsData. For retrieving the agents data, we can use call.enqueue. The enqueue method in Retrofit is used to make an asynchronous network request to an API endpoint. It is part of Retrofit's callback-based approach for handling network requests.
Now, you can use the data on your UI. For example, i created a list of agents like this using LazyColumn
@Composable
fun AgentsList(navHostController: NavHostController, viewModel: HomeViewModel) {
val agentsData = viewModel.agentsData.collectAsState()
LazyColumn(
verticalArrangement = Arrangement.spacedBy(10.dp),
modifier = Modifier
.background(blackV)
.fillMaxSize()
) {
items(count = agentsData.value.size) {
AgentsCard(agentsData.value[it], navHostController, viewModel)
}
}
}
And here’s is the complete example of the app that I made just by using the Valorant API
That’s all, thank you
Here’s the full source code that available on my github: https://github.com/Chndr-3/Valorant