MVVM Retrofit with Kotlin Coroutines in Android
This tutorial helps you to integrate retrofit library with kotlin coroutines and MVVM architecture in android applications. Coroutines are used to simplify async code to prevent blocking main thread for long running tasks in android.
Coroutines are used in many popular programming languages. Coroutines are light weight as compared to RxJava. Coroutines makes code very simple so beginners can easily understand. Kotlin Coroutines ensures that long running task should be done without blocking main thread in android applications.
Every application has main thread to perform ui operations. If we are doing too much work in this ui thread, then you can see “application is not responding” alert in android applications. To prevent this error, We should optimize our code using kotlin coroutines in android applications.
suspend is used to pause current coroutine execution and save all local variables. resume is used to resume paused coroutine from the place where it was paused. We can only call suspend functions. suspend and resume functions work together to replace callbacks.
Add required dependencies in your app level build.gradle file for Kotlin Coroutines in Android
implementation ‘androidx.lifecycle:lifecycle-common:2.2.0’
implementation ‘androidx.lifecycle:lifecycle-runtime:2.2.0’
implementation ‘android.arch.lifecycle:extensions:2.2.0’
implementation ‘androidx.lifecycle:lifecycle-livedata-ktx:2.2.0’
// retrofit
implementation ‘com.squareup.retrofit2:retrofit:2.6.0’
implementation ‘com.squareup.retrofit2:converter-gson:2.5.0’
// coroutines
implementation ‘org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5’
// ui
implementation ‘androidx.cardview:cardview:1.0.0’
implementation ‘androidx.recyclerview:recyclerview:1.1.0’
Create Activity Layout
<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=”match_parent”>
<androidx.recyclerview.widget.RecyclerView
android:id=”@+id/recycler_employees”
android:layout_width=”0dp”
android:layout_height=”0dp”
app:layout_constraintBottom_toBottomOf=”parent”
app:layout_constraintLeft_toLeftOf=”parent”
app:layout_constraintRight_toRightOf=”parent”
app:layout_constraintTop_toTopOf=”parent” />
</androidx.constraintlayout.widget.ConstraintLayout>
Create Adapter Row Layout
<androidx.cardview.widget.CardView xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:app=”http://schemas.android.com/apk/res-auto”
app:cardCornerRadius=”10dp”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_marginTop=”8dp”
android:layout_marginBottom=”8dp”
android:layout_marginStart=”15dp”
android:layout_marginEnd=”15dp”>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:padding=”10dp”>
<TextView
android:id=”@+id/txt_employee_name”
app:layout_constraintStart_toStartOf=”parent”
app:layout_constraintTop_toTopOf=”parent”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:textSize=”22sp”
android:textStyle=”bold” />
<TextView
android:id=”@+id/txt_employee_info1″
app:layout_constraintStart_toStartOf=”parent”
app:layout_constraintTop_toBottomOf=”@+id/txt_employee_name”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:textSize=”16sp” />
<TextView
android:id=”@+id/txt_employee_info2″
app:layout_constraintStart_toStartOf=”parent”
app:layout_constraintTop_toBottomOf=”@+id/txt_employee_info1″
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:textSize=”16sp” />
<TextView
android:id=”@+id/txt_employee_address”
app:layout_constraintStart_toStartOf=”parent”
app:layout_constraintTop_toBottomOf=”@+id/txt_employee_info2″
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:textSize=”16sp” />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
Create Response Classes to parse data from api
@SerializedName(“id”)
val employeeId: Int? = null,
@SerializedName(“name”)
val employeeName: String? = null,
@SerializedName(“username”)
val employeeUserName: String? = null,
@SerializedName(“email”)
val employeeEmail: String? = null,
@SerializedName(“address”)
val employeeAddressObject : EmployeeAddress? = null,
@SerializedName(“phone”)
val employeePhone: String? = null,
@SerializedName(“website”)
val employeeWebsite: String? = null
) : Serializable
@SerializedName(“street”)
val employeeStreet: String? = null,
@SerializedName(“suite”)
val employeeSuite: String? = null,
@SerializedName(“city”)
val employeeCity: String? = null,
@SerializedName(“zipcode”)
val employeeZipCode: String? = null
) : Serializable
Create Adapter Class for Coroutines Kotlin Android
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val inflater = LayoutInflater.from(context)
val view: View = inflater.inflate(R.layout.employee_row,parent,false)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return listEmployees.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val getEmployee = listEmployees.get(position)
holder.employeeName?.text = getEmployee.employeeName
holder.employeeInfo1?.text = getEmployee?.employeeUserName + ” | ” + getEmployee?.employeeEmail
holder.employeeInfo2?.text = getEmployee?.employeePhone + ” | ” + getEmployee?.employeeWebsite
val employeeAddressObj = getEmployee.employeeAddressObject
holder.employeeAddress?.text = employeeAddressObj?.employeeSuite + “,” + employeeAddressObj?.employeeStreet + “,” + employeeAddressObj?.employeeCity + “,” + employeeAddressObj?.employeeZipCode
}
class MyViewHolder(var view: View) : RecyclerView.ViewHolder(view){
var employeeName: TextView? = null
var employeeInfo1: TextView? = null
var employeeInfo2: TextView? = null
var employeeAddress: TextView? = null
init {
employeeName = view.findViewById(R.id.txt_employee_name)
employeeInfo1 = view.findViewById(R.id.txt_employee_info1)
employeeInfo2 = view.findViewById(R.id.txt_employee_info2)
employeeAddress = view.findViewById(R.id.txt_employee_address)
}
}
}
Create Utility Classes to handle data from api
companion object {
fun <T> success(data: T): ApiResponse<T> = ApiResponse(apiStatus = ApiStatus.SUCCESS, data = data, message = null)
fun <T> error(data: T?, message: String): ApiResponse<T> =
ApiResponse(apiStatus = ApiStatus.ERROR, data = data, message = message)
fun <T> loading(data: T?): ApiResponse<T> = ApiResponse(apiStatus = ApiStatus.LOADING, data = data, message = null)
}
}
SUCCESS,
ERROR,
LOADING
}
Create ViewModel Class With Kotlin Coroutines in Android
fun getEmployees() = liveData(Dispatchers.IO) {
emit(ApiResponse.loading(data = null))
try {
emit(ApiResponse.success(data = ApiClient.apiService.getEmployees()))
} catch (exception: Exception) {
emit(ApiResponse.error(data = null, message = exception.message ?: “Error Occurred!”))
}
}
}
Integrate ViewModel in Activity Class for Kotlin Coroutines in Android
private lateinit var listEmployees: MutableList<Employee>
private lateinit var employeesAdapter: EmployeesAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recycler_employees.layoutManager = LinearLayoutManager(this@MainActivity)
listEmployees = mutableListOf<Employee>()
employeesAdapter = EmployeesAdapter(this, listEmployees)
recycler_employees.adapter = employeesAdapter
val employeeViewModel = ViewModelProviders.of(this).get(EmployeeViewModel::class.java)
employeeViewModel.getEmployees().observe(this, Observer {
it?.let { apiResponse ->
when (apiResponse.apiStatus) {
ApiStatus.SUCCESS -> {
recycler_employees.visibility = View.VISIBLE
hideProgressBar()
apiResponse.data?.let {
listEmployees.clear()
listEmployees.addAll(it)
employeesAdapter.apply {
notifyDataSetChanged()
}
}
}
ApiStatus.ERROR -> {
recycler_employees.visibility = View.VISIBLE
hideProgressBar()
Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
}
ApiStatus.LOADING -> {
showProgressBar()
recycler_employees.visibility = View.GONE
}
}
}
})
}
}
