Android Core

MVVM Architecture in Android — Complete Guide with Example

1. What is MVVM Architecture?

MVVM stands for Model–View–ViewModel. It is a modern architectural pattern widely used in Android development. MVVM separates the UI logic from business logic and data handling, enabling **reactive UI updates** and **better testability**.

2. Key Components of MVVM

  • Model: Manages data and business logic. It fetches data from APIs, databases, or repositories.
  • View: Displays data to the user and observes data changes. It is “dumb” and contains minimal logic.
  • ViewModel: Mediates between the Model and View. It exposes observable data (LiveData or StateFlow) and handles UI-related logic without referencing Android UI classes.

3. MVVM Data Flow

  1. View observes LiveData / StateFlow in ViewModel.
  2. User interacts with the UI (View), triggering events in ViewModel.
  3. ViewModel calls the Model / Repository to fetch or update data.
  4. Model returns data to ViewModel.
  5. ViewModel updates LiveData / StateFlow.
  6. View automatically observes the change and updates the UI.

4. Advantages of MVVM

  • Separation of concerns — UI and business logic are decoupled.
  • Reactive UI updates via LiveData / StateFlow.
  • Easy to unit test ViewModel without Android dependencies.
  • Improves maintainability and scalability for large projects.

5. Example: Simple Login Feature Using MVVM

📁 Folder Structure


                    ├── model/
                    │   └── UserModel.kt
                    ├── repository/
                    │   └── UserRepository.kt
                    ├── viewmodel/
                    │   └── LoginViewModel.kt
                    ├── view/
                    │   └── LoginActivity.kt
                    └── activity_login.xml
                    

🧩 Model: UserModel.kt


                    package com.example.mvvm.model

                    data class UserModel(val username: String, val password: String) {
                        fun isValid(): Boolean {
                            return username.isNotEmpty() && password.length >= 6
                        }
                    }
                    

📦 Repository: UserRepository.kt


                    package com.example.mvvm.repository

                    import com.example.mvvm.model.UserModel

                    class UserRepository {
                        fun login(username: String, password: String): Boolean {
                            val user = UserModel(username, password)
                            return user.isValid()
                        }
                    }
                    

🧠 ViewModel: LoginViewModel.kt


                    package com.example.mvvm.viewmodel

                    import androidx.lifecycle.LiveData
                    import androidx.lifecycle.MutableLiveData
                    import androidx.lifecycle.ViewModel
                    import com.example.mvvm.repository.UserRepository

                    class LoginViewModel : ViewModel() {

                        private val repository = UserRepository()

                        private val _loginResult = MutableLiveData()
                        val loginResult: LiveData get() = _loginResult

                        fun login(username: String, password: String) {
                            if (username.isEmpty() || password.isEmpty()) {
                                _loginResult.value = "Fields cannot be empty"
                                return
                            }

                            val success = repository.login(username, password)
                            _loginResult.value = if (success) "Login successful!" else "Invalid credentials"
                        }
                    }
                    

🎨 View: activity_login.xml


                    <LinearLayout
                        xmlns:android="http://schemas.android.com/apk/res/android"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:orientation="vertical"
                        android:padding="24dp">

                        <EditText
                            android:id="@+id/etUsername"
                            android:hint="Username"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"/>

                        <EditText
                            android:id="@+id/etPassword"
                            android:hint="Password"
                            android:inputType="textPassword"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"/>

                        <Button
                            android:id="@+id/btnLogin"
                            android:text="Login"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"/>
                    </LinearLayout>
                    

🎬 Activity: LoginActivity.kt


                    package com.example.mvvm.view

                    import android.os.Bundle
                    import android.widget.Toast
                    import androidx.activity.viewModels
                    import androidx.appcompat.app.AppCompatActivity
                    import com.example.mvvm.R
                    import com.example.mvvm.viewmodel.LoginViewModel
                    import kotlinx.android.synthetic.main.activity_login.*

                    class LoginActivity : AppCompatActivity() {

                        private val viewModel: LoginViewModel by viewModels()

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

                            btnLogin.setOnClickListener {
                                val username = etUsername.text.toString()
                                val password = etPassword.text.toString()
                                viewModel.login(username, password)
                            }

                            viewModel.loginResult.observe(this) { result ->
                                Toast.makeText(this, result, Toast.LENGTH_SHORT).show()
                            }
                        }
                    }
                    

6. MVVM Advantages Over MVP / MVC

  • View is completely passive — UI updates reactively via LiveData / StateFlow.
  • ViewModel contains no Android framework dependencies — easier unit testing.
  • Supports lifecycle awareness out-of-the-box with Jetpack components.
  • Better separation of concerns for complex apps.

Conclusion

MVVM is the recommended modern Android architecture pattern. Using ViewModel, LiveData, and Repository enables reactive, testable, and maintainable applications. MVVM is especially powerful for apps that require clean separation between UI, business logic, and data layers, making it highly preferred for large-scale Android projects and CTO-level discussions.