Hey Technoz, hope you are having a fun time coding. Well, in this tutorial, we are going to see how to use Retrofit Library to make http requests to server and get the data in our app. We will use RecyclerView to display the list data which we fetch from server with Retrofit. In order to make this api call in a background, we are using Kotlin Coroutines which is a concurrency design pattern that you can use on Android to simplify code that executes asynchronously.
Let’s have a look at what type of app we are going to build.
What is Retrofit Library?
Retrofit library is a type-safe REST client library which eases our work of making network calls to apis. Also, it supports the Json data parsing and hence one of the most popular library and widely used. A company named Square, Inc. developed it.
Video Tutorial
If you would prefer video tutorial for Retrofit Library, here it is the step by step below, available on YouTube.
You can also continue with this text tutorial.
Add required dependencies
Lets add the required dependencies of retrofit library and coroutines lifecycle scope in your module-level build.gradle as follows:
//Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.7.0'
// Coroutine Lifecycle Scopes
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1"
So, after setting up the dependencies, we are good to go further. Don’t forget to add the Internet permission in AndroidManifest.xml as follows.
<uses-permission android:name="android.permission.INTERNET"/>
Build the layout
Now we are going to build a layout. As we will be showing list of users in MainActivity, we need to add a RecyclerView widget in activity_main.xml as follows.
<?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">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/users_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp"
tools:listitem="@layout/user_layout"/>
<ProgressBar
android:id="@+id/progress_bar"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="@+id/users_list_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/users_list_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
In the above code, along with the RecyclerView, we have also added a ProgressBar which we will show till the time api fetch the data from server. In the RecyclerView, we have added tools:listitem="@layout/user_layout"
this line which shows the how the layout will be shown in the preview pane. This is fully optional, but for this retrofit Library tutorial, we are doing it as it helps us to see the design preview. So, lets create a layout item file for individual list item (user_layout.xml).
<?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"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Leanne Graham"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:textSize="20sp"
android:textStyle="bold"/>
<TextView
android:id="@+id/email"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="4dp"
android:text="Sincere@april.biz"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="@+id/name"
app:layout_constraintTop_toBottomOf="@+id/name" />
<TextView
android:id="@+id/phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="4dp"
android:text="1-770-736-8031 x56442"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="@+id/email"
app:layout_constraintTop_toBottomOf="@+id/email"/>
<TextView
android:id="@+id/website"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="4dp"
android:text="hildegard.org"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="@+id/phone"
app:layout_constraintTop_toBottomOf="@+id/phone"/>
<TextView
android:layout_width="match_parent"
android:layout_height="1dp"
app:layout_constraintTop_toBottomOf="@+id/website"
android:background="@color/black"
android:layout_margin="5dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
So far we what we have done is the layout part. At this part if you run your app, then you won’t see anything. But dude, trust me Hard work pays off! 😜
Set up json data class
The data we are fetching from one online free service. We get a json sample data by simply hitting the url. For this project, we will be pointing to https://jsonplaceholder.typicode.com/users this which returns the sample list of users and we’ll show that in our app. For that, we first need to create a data class as per the json response fields. Now, create a kotlin data class User.kt as follows.
package net.softglobe.retrofittutorial
data class User(
val email: String,
val id: Int,
val name: String,
val phone: String,
val username: String,
val website: String
)
Quick tip:
There is a plugin for Android Studio named “JSON To Kotlin Class” which can generate the kotlin data class by just reading the json response. To install the plugin, go to File-> Settings-> Plugins an search “JSON To Kotlin Class”. Install it. Now you can just right click on package name -> new and you will get an option “Kotlin data class File from JSON”. You will get a dialog where you can paste the json response. Paste the response there and proceed further. The plugin will automatically create a data class with relevant fields. Well, for this retrofit library tutorial, we are using only 6 out of multiple fields in the response, so you can delete other fields.
Create the Adapter
As we are using the RecyclerView to show list items, we need a custom adapter. Create UserAdapter.kt as follows.
package net.softglobe.retrofittutorial
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
class UserAdapter(private val listener : (User) -> Unit) : ListAdapter<User, UserAdapter.ViewHolder>(DiffUserCallBack()){
inner class ViewHolder(private val containerView : View) : RecyclerView.ViewHolder(containerView){
init {
itemView.setOnClickListener {
listener.invoke(getItem(adapterPosition))
}
}
fun bind(user : User){
containerView.findViewById<TextView>(R.id.name).text = user.name
containerView.findViewById<TextView>(R.id.email).text = user.email
containerView.findViewById<TextView>(R.id.phone).text = user.phone
containerView.findViewById<TextView>(R.id.website).text = user.website
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val itemLayout = LayoutInflater.from(parent.context).inflate(R.layout.user_layout, parent, false)
return ViewHolder(itemLayout)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
class DiffUserCallBack : DiffUtil.ItemCallback<User>(){
override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
return oldItem == newItem
}
}
The above recyclerview is an optimized version which changes the view contents efficiently with the help of ViewHolder. Have a look at the following explanation:
- The class
UserAdapter
extendsListAdapter
of typeUser
and aViewHolder
and parameter asDiffUserCallBack()
. - Then we create an inner class
ViewHolder
to hold the view which is being shown and the code in the constructor invokes the current item by specifying theadapterPosition
. - The class
UserAdapter
then implements two overridden methods:onCreateViewHolder
: It inflates the custom item layout we created earlier and return the ViewHolder object by passing it to the ViewHolder inner class.onBindViewHolder
: It explicitly calls thebind()
function in the ViewHolder inner class.
bind()
function takes care of binding the appropriate values to the respective view objects. For ex. in this case, we are assigning the user’s name, email, phone and website to the TextViews.- Then we create a class
DiffUserCallBack
which extends the abstract classDiffUtil
and callsItemCallback<T>
static java method. We have to implement their two methods:areItemsTheSame
: Here we check if the items are same by comparing the unique fields (in this case, ‘id’) and returns true if they matches, false otherwise.areContentsTheSame
: Here we check the actual contents of the two objects and returns true if they are same.
Set up classes for Retrofit Library
Hooossshhh!!! Finally coming to the main part of this tutorial, setting up the things needed for Retrofit Library. The work mostly happens in terms of annotations. There are several type of requests like GET, POST, PUT, DELETE, etc. Here for this Retrofit Library Tutorial, we are using GET request. For that, we will create an api interface in which we will define the requests. Create interface ApiInterface.kt as follows:
package net.softglobe.retrofittutorial
import retrofit2.Response
import retrofit2.http.GET
interface ApiInterface {
@GET("/users")
suspend fun getUserData() : Response<List<User>>
}
We have created one function getUserData()
to get the user data from api and returns the Response object from retrofit which further contains <List<User>
. Above the function, we are specifying the @GET
annotation which signifies that this function will be making a GET request to server. For that, we need the api endpoint which will return the expected json data. In this case, it is /users which is defined inside @GET
annotation.
Furthermore, we need to create an instance of retrofit library which we can use to make api calls. But, instead of creating it separately, we are creating a singleton instance of retrofit library which we can use anywhere in the app. For that, create an singleton object RetrofitInstance.kt as follows:
package net.softglobe.retrofittutorial
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitInstance {
val api : ApiInterface by lazy {
Retrofit.Builder()
.baseUrl("https://jsonplaceholder.typicode.com")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiInterface::class.java)
}
}
In the above file, we are building retrofit library instance using Retrofit.Builder()
.
baseUrl()
: defines the base url where the api is located. It does not include endpoint and every request will be sent to this base url with the endpoints further.addConverterFactory()
: This method sets the converter method for serialization and deserialization of objects and we passGsonConverterFactory.create()
which creates an instance using Gson for conversion.build()
: It creates the Retrofit instance using the configured values.create()
: It creates an implementation of the API endpoints defined by the interface passed to it.
Finally, Configure MainActivity
Now coming to the last file in this retrofit library tutorial, which contains the driver code that performs the task and binds everything together. Lets open MainActivity.kt and put the following code in it.
package net.softglobe.retrofittutorial
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.ProgressBar
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val loading = findViewById<ProgressBar>(R.id.progress_bar)
lifecycleScope.launchWhenCreated {
loading.visibility = View.VISIBLE
val response = try{
RetrofitInstance.api.getUserData()
}catch (e: Exception){
Log.e(TAG, "Exception: $e")
loading.visibility = View.GONE
return@launchWhenCreated
}
if (response.isSuccessful && response.body() != null){
Toast.makeText(this@MainActivity, ""+response.body()?.size+" users loaded!", Toast.LENGTH_SHORT).show()
val usersRecyclerView = findViewById<RecyclerView>(R.id.users_list_view).apply {
adapter = UserAdapter(){it}
layoutManager = LinearLayoutManager(this@MainActivity)
setHasFixedSize(true)
}
(usersRecyclerView.adapter as UserAdapter).submitList(response.body())
loading.visibility = View.GONE
} else {
Toast.makeText(this@MainActivity, "Something went wrong!", Toast.LENGTH_SHORT).show()
loading.visibility = View.GONE
}
}
}
}
In above file, the code inside lifecycleScope.launchWhenCreated
block will be run code inside on a background thread and we are making a call to the RetrofitInstance.api.getUserData()
under the try…catch block and assigning the result to “response” variable.
Furthermore, if the request is successful and returns non-null list, then we assigns the UserAdapter
adapter to the “adapter” parameter of RecyclerView and then submits the list which we got from the api response using submitList(response.body())
. We start displaying the ProgressBar at the start of request and hides it once the request is finished or in case some exception has occurred.
That’s it about the tutorial! your app is ready to do its work. Now try opening the app and see your results. Congratulations! you learnt how to use Retrofit Library to fetch a data from api.
Download Source code
If you want to skip going through the tutorial, then just go ahead and download the source code by visiting following link:
Source Code Link
If you have any questions or facing some issues implementing above tutorial, feel free to let me know in the comments section below. I will try my best to get back to you. Meet you in the next tutorial, till then bye and Happy Coding!
Something extra from me… ☺️
I would like to offer something extra from my side. Would you like to take it? If yes, then here it is. Technopoints has started its official WhatsApp Group where programming enthusiasts interact with each others, solve doubts, and learn together. There you will be in a group of like-minded people who are eager to learn, share knowledge, ask doubts and solve others doubts too. Additionally, there I share some of the rare programming tips related to Android development, the solution to the challenges I faced while making app projects which look my lot of time and I got stuck on it. With those tips, you will avoid common mistakes and conquer the hurdles of your programming journey.
Agree? Great! here is the invite link:
Click here to join the Technopoints WhatsApp Group!
It is open for programmers all over the Globe. Lets connect, share knowledge and grow together.
Good Night! Wondering why I said Good Night? because most of us are owls who are busy with our codes mostly late night. 😂 Including Me, writing this post late night…😁
2 thoughts on “Retrofit Library tutorial with RecyclerView in Kotlin”