
MVVM Architecture with Jetpack Compose in Android Programming

What is MVVM?
MVVM (Model-View-ViewModel) is an architectural pattern used in modern Android programming. Architectural patterns offer significant benefits in terms of writing cleaner code, increasing code readability, and making the code maintainable and open to development.
The key to using MVVM effectively lies in understanding how to place application code into the correct classes and how these classes interact.
- View: Contains the UI code (Activity, Fragment, Jetpack Compose) and XML. It sends user actions to the ViewModel but does not receive a direct response. To receive a response, it uses the observables provided by the ViewModel.
- Note: Since UI in Jetpack Compose has a declarative structure, data changes from the ViewModel automatically reflect in the UI components, creating a more dynamic user interface.
- Model: Represents the application’s data and business logic. The Model and ViewModel structures work together to fetch and save data.
- ViewModel: Acts as a bridge between the View and the Model. The ViewModel does not know which View it is interacting with because it has no direct connection with the View. Therefore, the ViewModel is unaware of the View it interacts with.
Advantages and Disadvantages of MVVM
Advantages | Disadvantages |
Easy to test and maintain. | It can be complex for simple projects. |
Scalable, allowing for easy code development. | Debugging can be harder compared to imperative (traditional) code. |
It has a sustainable structure. |
Example MVVM Architecture
In the Model layer of our project, we have a data class containing data types and variables, and a repository class that simplifies API usage, particularly for fetching user data.
data class UserData(
var name: String,
var age: Int
)class UserRepository {
suspend fun fetchUserData(): UserData {
delay(2000)
return UserData("Mary", 22)
}
}
Note: If you decide to use an API in your project, make sure to use Retrofit and Dagger Hilt. This will simplify data management.
Now, let’s look at the HomeViewModel class in the ViewModel layer:
class HomeViewModel : ViewModel() {
val userRepository: UserRepository = UserRepository()
private val _userData = MutableLiveData<UserData>()
val userData: LiveData<UserData> = _userDataprivate val _isLoading = MutableLiveData<Boolean>(false)
val isLoading: LiveData<Boolean> = _isLoadingfun getUserData() {
viewModelScope.launch {
val userResult = userRepository.fetchUserData()
_userData.postValue(userResult)
_isLoading.postValue(false)
}
}
}
Here, viewModelScope.launch starts an asynchronous operation, so the UI is not blocked. The fetchUserData() function from the UserRepository class in the Model layer is used to fetch user data. Using postValue, we safely send the LiveData value to the main thread while running in the background with a coroutine.
In the View layer, we create a UI view using Jetpack Compose:
@Composable
fun HomePage(modifier: Modifier = Modifier, viewModel: HomeViewModel) {val userData = viewModel.userData.observeAsState()
val isLoading = viewModel.isLoading.observeAsState()
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = {
viewModel.getUserData()
}) { Text(text = "Get Data") }if (isLoading.value == true) {
CircularProgressIndicator()
} else {
userData.value?.name?.let {
Text(text = "Name: $it")
}
userData.value?.age?.let {
Text(text = "Age: $it")
}
}
}
}
The HomePage function listens to the data from the ViewModel and updates the UI. When the user clicks the “Get Data” button, viewModel.getUserData() is called, and the data fetching process begins.
The loading state (isLoading) becomes true, and a CircularProgressIndicator appears on the screen. After a 2-second delay (delay(2000)), the data is sent to the UI, and the loading state becomes false.
As you can see here, the UI only observes the ViewModel and responds automatically to changes.