MVP Architecture in Android — Complete Guide with Example
1. What is MVP Architecture?
MVP stands for Model–View–Presenter. It evolved from the classic MVC pattern to solve Android’s problem of tight coupling between the View and Controller. In MVP, the Presenter acts as the middle layer between the View and Model, making the architecture more testable, modular, and maintainable.
2. Key Components of MVP
- Model: Manages data and business logic. It can fetch data from a database, network, or API, and expose methods to the Presenter.
- View: Handles UI elements and user interactions. It’s passive — meaning it only displays data and sends user actions to the Presenter.
- Presenter: Connects the View and the Model. It retrieves data from the Model and updates the View accordingly. It contains no Android framework code, which makes it easy to test.
3. MVP Data Flow
- User interacts with the View (e.g., clicks a button).
- The View delegates the event to the Presenter.
- The Presenter requests data or performs logic via the Model.
- The Model returns data to the Presenter.
- The Presenter updates the View with the new data.
4. Advantages of MVP
- Improved testability since the Presenter doesn’t depend on Android SDK.
- Better separation of concerns than MVC.
- Reusability — Presenter can be reused with different Views.
- Clean and maintainable architecture for medium to large projects.
5. Example: Simple Login Feature Using MVP
Below is an example implementation of MVP pattern for a login screen.
📁 Folder Structure
├── model/
│ └── UserModel.kt
├── presenter/
│ └── LoginPresenter.kt
├── view/
│ ├── LoginView.kt
│ └── LoginActivity.kt
└── activity_login.xml
🎨 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>
🧩 Model: UserModel.kt
package com.example.mvp.model
class UserModel {
fun isValidUser(username: String, password: String): Boolean {
return username.isNotEmpty() && password.length >= 6
}
}
🧠 Presenter: LoginPresenter.kt
package com.example.mvp.presenter
import com.example.mvp.model.UserModel
import com.example.mvp.view.LoginView
class LoginPresenter(private val view: LoginView) {
private val userModel = UserModel()
fun onLoginClicked(username: String, password: String) {
if (username.isEmpty() || password.isEmpty()) {
view.showError("Fields cannot be empty")
return
}
if (userModel.isValidUser(username, password)) {
view.showSuccess("Login successful!")
} else {
view.showError("Invalid username or password")
}
}
}
🖥️ View Interface: LoginView.kt
package com.example.mvp.view
interface LoginView {
fun showSuccess(message: String)
fun showError(message: String)
}
🎬 Activity: LoginActivity.kt
package com.example.mvp.view
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.mvp.R
import com.example.mvp.presenter.LoginPresenter
import kotlinx.android.synthetic.main.activity_login.*
class LoginActivity : AppCompatActivity(), LoginView {
private lateinit var presenter: LoginPresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
presenter = LoginPresenter(this)
btnLogin.setOnClickListener {
val username = etUsername.text.toString()
val password = etPassword.text.toString()
presenter.onLoginClicked(username, password)
}
}
override fun showSuccess(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
override fun showError(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
}
6. Disadvantages of MVP
- Can lead to a large number of interfaces and boilerplate code.
- Presenter can grow too big if not managed properly (called “Massive Presenter”).
- Still lacks lifecycle awareness compared to MVVM.
Conclusion
The MVP pattern provides a clean and testable structure for Android apps. It improves upon MVC by decoupling business logic from UI logic, allowing for easier unit testing and modular development. However, in modern Android, developers often move towards MVVM and Clean Architecture using Jetpack components for even better lifecycle handling.