top of page

MVVM Architecture in Android Using Kotlin: A Comprehensive Guide with Code Examples

MVVM (Model-View-ViewModel) is an architectural pattern used to develop user interfaces. It separates the user interface (View) from the business logic (ViewModel) and the data model (Model). This separation of concerns helps to make the code more maintainable and testable.

In the MVVM architecture, the View is responsible for displaying the user interface, the ViewModel contains the business logic and interacts with the Model to retrieve and update data, and the Model represents the data source or repository.




Introduction:

In today's world, mobile applications are becoming more complex and challenging to maintain. The MVVM (Model-View-ViewModel) architecture has emerged as a popular choice among developers for creating scalable and maintainable Android applications. MVVM provides a clear separation of concerns, making it easier to manage the codebase, enhance the application's testability, and reduce the complexity of UI-related tasks. In this blog post, we will discuss the MVVM architecture in detail, along with its implementation in an Android application using Kotlin. We will go through each component of the MVVM architecture and explain how to build a robust and well-organized Android application using this pattern.


Prerequisites:

Before diving into the MVVM architecture implementation in an Android application using Kotlin, you should have the following knowledge and skills:

  1. A basic understanding of Android development using Kotlin.

  2. Familiarity with Android Studio and its features.

  3. Knowledge of object-oriented programming concepts such as inheritance, polymorphism, and encapsulation.

  4. Basic knowledge of design patterns such as Singleton, Factory, and Observer.

  5. Knowledge of the Android Architecture Components, including LiveData and ViewModel.

  6. Knowledge of networking concepts and libraries such as Retrofit.

  7. Familiarity with database concepts and libraries such as Room.

Having these prerequisites will help you understand the implementation of the MVVM architecture in an Android application using Kotlin better.


Creating a Model:

The Model represents the data layer of an application in the MVVM architecture. In an Android application, it can be a database, a network service, or a combination of both. In this section, we will create a model using Room and Retrofit libraries for a sample application that displays a list of users.


Define the data model class:

 
@Entity(tableName = "users")data class User(
    @PrimaryKey val id: Int,
    val name: String,
    val email: String,
    val phone: String,
    val website: String
)

In this code, we define a data class called "User" and annotate it with "@Entity" to indicate that it's a table in the database. We also specify the table name as "users". The "id" field is marked as the primary key using the "@PrimaryKey" annotation.


Create the data access object (DAO):

 
@Dao
interface UserDao {
    @Query("SELECT * FROM users")fun getUsers(): LiveData<List<User>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)suspend fun insertUsers(users: List<User>)
}

In this code, we define a DAO interface called "UserDao" and annotate it with "@Dao". We define two methods, "getUsers()" and "insertUsers(users: List<User>)". The "getUsers()" method returns a LiveData object that contains a list of User objects. The "@Query" annotation is used to specify the SQL query to fetch data from the database. The "insertUsers()" method inserts a list of User objects into the database using the "@Insert" annotation. We also specify "OnConflictStrategy.REPLACE" to handle conflicts that may arise during data insertion.


Create the Room database:

 
 @Database(entities = [User::class], version = 1)abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

In this code, we define an abstract class called "AppDatabase" and annotate it with "@Database". We specify the "entities" parameter as an array of User objects, which indicates that our database will have a single table called "users". We also specify the "version" parameter as 1 to indicate the current version of the database. We define an abstract method called "userDao()" that returns a UserDao object.


Create the repository:


class UserRepository(private val userDao: UserDao, private val userService: UserService) {

    val users: LiveData<List<User>> = userDao.getUsers()

    suspend fun refreshUsers() {
        withContext(Dispatchers.IO) {
            val users = userService.getUsers()
            userDao.insertUsers(users)
        }
    }
}

In this code, we define a UserRepository class that contains a UserDao object and a UserService object. The UserRepository class has a "users" property that returns a LiveData object containing a list of User objects fetched from the database using the UserDao object. We also define a suspend function called "refreshUsers()" that fetches user data from a network service using the UserService object and inserts it into the database using the UserDao object.

With these steps, we have created the Model component of the MVVM architecture using Room and Retrofit libraries.


Creating the View in XML in MainActivity:

In the MVVM architecture, the View is responsible for displaying the user interface and interacting with the user. In an Android application, the View is implemented using XML layout files. In this section, we will create the View component of the MVVM architecture for a sample application that displays a list of users.

  1. Open the activity_main.xml layout file:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"><androidx.recyclerview.widget.RecyclerView
android:id="@+id/userList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:scrollbars="vertical"
android:visibility="visible" />
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
</LinearLayout>

In this code, we define a LinearLayout as the root element and set its orientation to vertical. We add a RecyclerView with the ID "userList" to display the list of users. We also add a ProgressBar with the ID "progressBar" to indicate loading progress.

  1. Define the RecyclerView item layout:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/userName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp" />
<TextView
android:id="@+id/userEmail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="@android:color/darker_gray" />
<TextView
android:id="@+id/userPhone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="@android:color/darker_gray" />
<TextView
android:id="@+id/userWebsite"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="@android:color/darker_gray" />
</LinearLayout>

In this code, we define a LinearLayout as the root element and set its orientation to vertical. We add four TextViews with IDs "userName", "userEmail", "userPhone", and "userWebsite" to display user data.

With these steps, we have created the View component of the MVVM architecture using XML layout files in the MainActivity.


Creating RecyclerView Adapter Class in Kotlin:

In the MVVM architecture, the Adapter is responsible for binding the data to the View and creating the ViewHolders to display the data. In this section, we will create the Adapter component of the MVVM architecture for a sample application that displays a list of users.

  1. Create a new Kotlin class named UserAdapter:

 
 class UserAdapter : RecyclerView.Adapter<UserAdapter.ViewHolder>() {

    private var userList = emptyList<User>()

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(user: User) {
            itemView.userName.text = user.name
            itemView.userEmail.text = user.email
            itemView.userPhone.text = user.phone
            itemView.userWebsite.text = user.website
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.user_item, parent, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(userList[position])
    }

    override fun getItemCount(): Int = userList.size

    fun setData(userList: List<User>) {
        this.userList = userList
        notifyDataSetChanged()
    }
}

In this code, we define a UserAdapter class that extends RecyclerView.Adapter and takes a ViewHolder as a parameter. We also define a ViewHolder class that takes an item view as a parameter and binds the user data to the TextViews.

We override the onCreateViewHolder() method to inflate the user_item layout and return a ViewHolder object. We override the onBindViewHolder() method to bind the user data to the ViewHolder object.

We override the getItemCount() method to return the size of the userList. We define a setData() method that takes a List of users as a parameter and sets the userList and calls notifyDataSetChanged() to update the RecyclerView.

  1. Define the user_item layout:

 
 <?xml version="1.0" encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/userName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp" />
<TextView
android:id="@+id/userEmail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="@android:color/darker_gray" />
<TextView
android:id="@+id/userPhone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="@android:color/darker_gray" />
<TextView
android:id="@+id/userWebsite"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="@android:color/darker_gray" />
</LinearLayout>

In this code, we define a LinearLayout as the root element and set its orientation to vertical. We add four TextViews with IDs "userName", "userEmail", "userPhone", and "userWebsite" to display user data.


With these steps, we have created the Adapter component of the MVVM architecture using Kotlin code in the UserAdapter class.


Creating ViewModel Class in Kotlin:

In the MVVM architecture, the ViewModel is responsible for managing the data and providing it to the View. In this section, we will create the ViewModel component of the MVVM architecture for a sample application that displays a list of users.


Create a new Kotlin class named UserViewModel:


 class UserViewModel : ViewModel() {

    private val userRepository = UserRepository()

    private val _userList = MutableLiveData<List<User>>()
    val userList: LiveData<List<User>>
        get() = _userList

    init {
        getUserList()
    }

    private fun getUserList() {
        viewModelScope.launch {
            val users = userRepository.getUserList()
            _userList.value = users
        }
    }
}

In this code, we define a UserViewModel class that extends ViewModel. We create a UserRepository object to fetch the user data from a data source.

We define a private MutableLiveData variable named "_userList" and a public LiveData variable named "userList" that returns the "_userList" value. We initialize the "_userList" value by calling the getUserList() function in the init block. The getUserList() function fetches the user data using the userRepository object in a coroutine and sets the "_userList" value to the fetched user data.


Define the UserRepository class:


class UserRepository {

    suspend fun getUserList(): List<User> {
        delay(2000)
        return listOf(
            User(1, "Leanne Graham", "Sincere@april.biz", "1-770-736-8031 x56442", "hildegard.org"),
            User(2, "Ervin Howell", "Shanna@melissa.tv", "010-692-6593 x09125", "anastasia.net"),
            User(3, "Clementine Bauch", "Nathan@yesenia.net", "1-463-123-4447", "ramiro.info"),
            User(4, "Patricia Lebsack", "Julianne.OConner@kory.org", "493-170-9623 x156", "kale.biz"),
            User(5, "Chelsey Dietrich", "Lucio_Hettinger@annie.ca", "(254)954-1289", "demarco.info")
        )
    }
}

In this code, we define a UserRepository class that has a suspend function named "getUserList" to fetch the user data from a data source. In this sample application, we simulate the data source by returning a list of hardcoded users with a delay of 2 seconds.


With these steps, we have created the ViewModel component of the MVVM architecture using Kotlin code in the UserViewModel class and the UserRepository class to fetch the user data from a data source.



Creating ViewModel Factory in Kotlin:

In the MVVM architecture, the ViewModelFactory is responsible for creating and providing the ViewModel instance to the View.

In this section, we will create the ViewModelFactory component of the MVVM architecture for a sample application that displays a list of users.


Create a new Kotlin class named UserViewModelFactory:

 
class UserViewModelFactory(private val userRepository: UserRepository) : ViewModelProvider.Factory {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(UserViewModel::class.java)) {
            return UserViewModel(userRepository) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

In this code, we define a UserViewModelFactory class that implements the ViewModelProvider.Factory interface. We define a constructor that takes a UserRepository object as a parameter.


We override the create() function of the ViewModelProvider.Factory interface to create and return a UserViewModel instance. We check if the passed class is assignable from UserViewModel, and if it is, we create and return a new instance of the UserViewModel class by passing the UserRepository object as a parameter.


If the passed class is not assignable from UserViewModel, we throw an IllegalArgumentException.


With this step, we have created the ViewModelFactory component of the MVVM architecture using Kotlin code in the UserViewModelFactory class.


Connecting the code to the MainActivity:

In this section, we will connect the code for the model, view, and ViewModel to the MainActivity. We will use a RecyclerView to display the list of users fetched from the data source.


In the MainActivity class, create a UserViewModel object and a UserViewModelFactory object:


class MainActivity : AppCompatActivity() {

    private lateinit var userAdapter: UserAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val userRepository = UserRepository()
        val userViewModelFactory = UserViewModelFactory(userRepository)
        val userViewModel = ViewModelProvider(this, userViewModelFactory).get(UserViewModel::class.java)

        userAdapter = UserAdapter()

        val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = userAdapter

        userViewModel.userList.observe(this, { userList ->
            userList?.let {
                userAdapter.submitList(userList)
            }
        })
    }
}

In this code, we create a UserRepository object, a UserViewModelFactory object by passing the UserRepository object as a parameter, and a UserViewModel object by passing the UserViewModelFactory and the MainActivity as parameters.


We also create a UserAdapter object to display the list of users in the RecyclerView. We set the RecyclerView's layout manager to a LinearLayoutManager and its adapter to the UserAdapter.


We observe the userList LiveData variable in the UserViewModel object and call the submitList() function of the UserAdapter to update the RecyclerView's list of users when the userList changes.

  1. In the activity_main.xml layout file, add a RecyclerView:


<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
 />

In this code, we define a RecyclerView with an id of "recyclerView" and set its layout_width and layout_height to match_parent.


With these steps, we have connected the code for the model, view, and ViewModel to the MainActivity using a RecyclerView to display the list of users.



Conclusion

the MVVM architecture is a powerful and widely used pattern in Android development.

It allows developers to separate concerns and make their code more modular, reusable, and testable. In this blog post, we have covered the basics of MVVM architecture and provided a step-by-step guide with code examples on how to implement it in an Android app using Kotlin.


We started by explaining the three main components of the MVVM architecture: the Model, View, and ViewModel. We then went on to discuss the benefits of using this architecture pattern, such as increased maintainability, scalability, and testability.

We provided code examples on how to create the Model class, the View layout in XML, the RecyclerView Adapter, the ViewModel class, and the ViewModel Factory.

We also showed how to connect these components to the MainActivity using a RecyclerView to display a list of users.

By following the steps outlined in this guide, developers can build high-quality Android apps with the MVVM architecture pattern, while keeping their code organized, maintainable, and scalable.


If you need further assistance with implementing the MVVM architecture in your Android app, or any other software development project, CodersArts is here to help. Our team of experienced developers can provide expert guidance and support to help you achieve your goals. Contact us today to learn more about our services and how we can help bring your project to life.


Thank you



The journey of solving bugs and completing projects on time in Kotlin can be challenging and lonely. If you need help regarding other sides of Kotlin, we’re here for you!





Drop an email to us at contact@codersarts.com with the Project title, deadline, and requirement files. Our email team will revert back promptly to get started on the work.

Comments


bottom of page