MVP vs MVVM in Android: A Concise Comparison
When building Android apps, MVP (Model–View–Presenter) and MVVM (Model–View–ViewModel) are two popular architectural patterns that help you separate concerns, improve testability, and make your codebase easier to maintain.
Below is a concise comparison focused on responsibilities, data flow, testability, and typical Android usage.
1. Core Concepts
MVP (Model–View–Presenter)
- View: UI layer (Activity/Fragment/View). Displays data and forwards user actions to the Presenter.
- Presenter: Middle layer. Contains presentation logic, talks to the Model, and updates the View via an interface.
- Model: Data layer (repositories, data sources, business logic).
Key idea: The Presenter directly references the View (usually via an interface), and the View calls the Presenter. This creates a two-way relationship.
MVVM (Model–View–ViewModel)
- View: UI layer (Activity/Fragment/Compose UI). Observes data from the ViewModel and renders it.
- ViewModel: Holds UI state and logic. Exposes observable data (LiveData, StateFlow, etc.) and handles events.
- Model: Data layer (repositories, data sources, business logic).
Key idea: The View observes the ViewModel. The ViewModel does not hold a reference to the View; communication is via observable state and events.
2. Responsibilities & Data Flow
MVP Flow
- User interacts with View (e.g., button click).
- View calls a method on Presenter (e.g.,
presenter.onLoginClicked()). - Presenter calls Model (e.g., repository) to perform work.
- Model returns result to Presenter.
- Presenter updates View through a View interface (e.g.,
view.showSuccess()).
Coupling: Presenter ↔ View (interface-based, but still a direct reference).
MVVM Flow
- User interacts with View.
- View calls a method on ViewModel (e.g.,
viewModel.onLoginClicked()), or triggers a bound event. - ViewModel calls Model to perform work.
- Model returns result to ViewModel.
- ViewModel updates observable state (LiveData/Flow/State).
- View observes this state and re-renders automatically.
Coupling: View observes ViewModel; ViewModel has no direct reference to View.
3. Testability
MVP
- Presenter is easy to unit test:
- Mock the View interface.
- Mock the Model/repository.
- View (Activity/Fragment) is still hard to test due to Android framework dependencies.
MVVM
- ViewModel is highly testable:
- Pure Kotlin/Java class, no Android context required (ideally).
- Exposes state as observable streams; you can assert on emitted values.
- Works very well with coroutines, Flow, and LiveData.
4. Android Ecosystem Fit
MVP
- Popular before official architecture components.
- Requires manual wiring of View–Presenter contracts.
- Less alignment with modern Jetpack libraries.
MVVM
- Officially encouraged by Google via Android Architecture Components.
- Integrates naturally with:
ViewModel(lifecycle-aware)LiveData/StateFlow/SharedFlow- Data Binding / View Binding / Jetpack Compose state
- Better support for configuration changes (ViewModel survives Activity recreation).
5. Handling Configuration Changes
MVP
- Presenter is often retained manually (e.g., using retained Fragments, DI scopes, or custom mechanisms).
- Risk of memory leaks if the Presenter keeps a strong reference to a destroyed View.
MVVM
ViewModelis lifecycle-aware and survives configuration changes by design.- ViewModel does not reference the View directly, reducing leak risk.
6. Typical Code Shape
MVP (simplified)
- View interface:
LoginViewwith methods likeshowLoading(),showError(message). - Presenter:
LoginPresenterwithonLoginClicked(user, pass). - Activity implements
LoginViewand holds aLoginPresenterreference.
MVVM (simplified)
- ViewModel:
LoginViewModelexposesloginState: LiveData<LoginUiState>andonLoginClicked(user, pass). - Activity/Fragment/Compose observes
loginStateand updates UI.
7. When to Use Which
Choose MVP if:
- You work on a legacy codebase already using MVP.
- You want very explicit View–Presenter contracts and are comfortable managing lifecycle and references.
Choose MVVM if:
- You are building a new Android app.
- You want to leverage Jetpack ViewModel, LiveData/Flow, and Compose.
- You prefer unidirectional data flow and lifecycle-aware components.
In modern Android development, MVVM is generally preferred because it aligns with official guidance, scales better with reactive patterns, and simplifies lifecycle handling.
8. Summary Table
| Aspect | MVP | MVVM |
|----------------------------|-------------------------------------------|-----------------------------------------------------|
| Middle layer | Presenter | ViewModel |
| View reference | Presenter holds View (via interface) | ViewModel does not hold View |
| Communication | Direct method calls both ways | View observes ViewModel state |
| Lifecycle handling | Manual (risk of leaks) | Built-in via ViewModel |
| Testability (logic) | Good (Presenter) | Excellent (ViewModel + reactive streams) |
| Android ecosystem support | Mostly manual patterns | First-class via Jetpack Architecture Components |
| Recommended for new apps | Rarely (mostly legacy/constraints) | Yes, especially with Jetpack and Compose |
/// MVP Example (Kotlin, simplified)
interface LoginView {
fun showLoading()
fun hideLoading()
fun showError(message: String)
fun navigateToHome()
}
class LoginPresenter(
private val view: LoginView,
private val repository: AuthRepository
) {
fun onLoginClicked(username: String, password: String) {
view.showLoading()
repository.login(username, password,
onSuccess = {
view.hideLoading()
view.navigateToHome()
},
onError = { error ->
view.hideLoading()
view.showError(error.message ?: "Unknown error")
}
)
}
}
/// MVVM Example (Kotlin, simplified with LiveData)
sealed class LoginUiState {
object Idle : LoginUiState()
object Loading : LoginUiState()
object Success : LoginUiState()
data class Error(val message: String) : LoginUiState()
}
class LoginViewModel(
private val repository: AuthRepository
) : ViewModel() {
private val _uiState = MutableLiveData<LoginUiState>(LoginUiState.Idle)
val uiState: LiveData<LoginUiState> = _uiState
fun onLoginClicked(username: String, password: String) {
_uiState.value = LoginUiState.Loading
viewModelScope.launch {
try {
repository.loginSuspend(username, password)
_uiState.value = LoginUiState.Success
} catch (e: Exception) {
_uiState.value = LoginUiState.Error(e.message ?: "Unknown error")
}
}
}
}
// In Activity/Fragment
// viewModel.uiState.observe(viewLifecycleOwner) { state ->
// when (state) {
// is LoginUiState.Idle -> { /* show idle UI */ }
// is LoginUiState.Loading -> { /* show loading */ }
// is LoginUiState.Success -> { /* navigate to home */ }
// is LoginUiState.Error -> { /* show error */ }
// }
// } MVP and MVVM both aim to separate concerns, but they trade off manual control versus reactive automation.
- MVP (Model–View–Presenter)
- The Presenter directly commands the View via an interface.
- UI updates are manual: the Presenter calls methods like
view.showUsers(users). - There is a tight, explicit View–Presenter contract (one Presenter per View interface).
- Testability is high: you mock the View interface and test the Presenter’s logic.
- Best fit for:
- Simple or moderately complex UIs.
- Legacy or incremental refactors where you peel logic away from UI screen by screen.
- Resource‑constrained environments or platforms without strong data binding support.
- MVVM (Model–View–ViewModel)
- The View observes the ViewModel; the ViewModel exposes state and commands.
- UI updates are automatic via data binding / observables.
- Very loose coupling: the ViewModel has no reference to the View.
- Testability is excellent: ViewModels are pure, UI‑agnostic units.
- Best fit for:
- Complex, reactive, data‑driven UIs (dashboards, live feeds, rich forms).
- Teams and frameworks with strong data binding support.
- Projects where high automated test coverage and parallel UI/logic work are priorities.
Core trade‑off:
- MVP → more boilerplate, but precise, explicit control over every UI change.
- MVVM → more setup and a steeper learning curve, but automatic, reactive UI sync and cleaner testability.
In practice:
- Choose MVP for simple screens, legacy migrations, or constrained devices.
- Choose MVVM for modern, complex, real‑time or highly interactive applications where data binding pays off.