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

// lifecycle
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

<?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=”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

<?xml version=”1.0″ encoding=”utf-8″?>
<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

data class Employee(

@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

 
data class EmployeeAddress(

@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

class EmployeesAdapter(private val context: Context, private var listEmployees: MutableList<Employee>) : RecyclerView.Adapter<EmployeesAdapter.MyViewHolder>() {

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

data class ApiResponse<out T>(val apiStatus: ApiStatus, val data: T?, val message: String?) {
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)
}
}

 
enum class ApiStatus {
    SUCCESS,
    ERROR,
    LOADING
}
 
 

Create ViewModel Class With Kotlin Coroutines in Android

class EmployeeViewModel() : ViewModel() {

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

class MainActivity : AppCompatActivity() {

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
    }
}
}
})

}

}

 
 
 
kotlin coroutines android
 

Leave a Reply