Getting Started with MVP (Model View Presenter) on Android
In this hands-on tutorial, we apply a design pattern called MVP, short for Model-View-Presenter, to an Android application. By Jinn Kim.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Getting Started with MVP (Model View Presenter) on Android
25 mins
- Getting Started
- MVC: The Traditional Approach
- What Is Wrong With This Approach?
- MVP: An Alternative Approach
- Refactoring Umbrella
- Organizing Features
- Adding Dependency Injection
- Defining the Contract
- Defining the Presenter
- Writing the View
- Testing the Presenter
- Pitfalls of MVP
- View Lifecycle
- Presenter Code Reuse
- Presenter State
- Where to Go From Here?
Testing the Presenter
Testing your app will be much easier once you adopt MVP as an architecture.
To start, add Mockito to your testing dependencies in the app module build.gradle file:
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
// Add mockito to your project
testImplementation 'org.mockito:mockito-core:2.22.0'
If you’re not familiar with Mockito, just know that it’s a mocking framework for writing unit tests. If you want a deep dive into the framework, check out our tutorial all about it.
There is an empty unit test file in the test package named MainPresenterTest.kt. Update the contents of the file to be as follows:
package com.raywenderlich.android.rwandroidtutorial
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.junit.MockitoJUnitRunner
@RunWith(MockitoJUnitRunner::class)
class MainPresenterTest {
@Mock
private lateinit var mockMainActivity: MainContract.View
private val dependencyInjector: DependencyInjector = StubDependencyInjector()
private var presenter: MainPresenter? = null
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
presenter = MainPresenter(mockMainActivity, dependencyInjector)
}
@After
fun tearDown() {
presenter?.onDestroy()
}
@Test
fun testOnViewCreatedFlow() {
presenter?.onViewCreated()
verify(mockMainActivity).displayWeatherState(WeatherState.RAIN)
}
}
class StubDependencyInjector : DependencyInjector {
override fun weatherRepository(): WeatherRepository {
return StubWeatherRepository()
}
}
class StubWeatherRepository : WeatherRepository {
override fun loadWeather(): Weather {
var weather = Weather("xxx")
var rain = Rain()
rain.amount = 10
weather.rain = rain
return weather
}
}
You’ve added a test class named MainPresenterTest
to the file, and added a single test to the class named testOnViewCreatedFlow()
. You added the mocking, stubbing, setup and tear down code you need to run the one test.
As the test illustrates, you can mock the view interface (which in the app is implemented by MainActivity
) and test your business logic with a simple unit test. The presenter is absent of Android framework components, and thus you can test it using lightweight unit tests rather than instrumented UI tests. The business logic in Umbrella, i.e. a rain icon should be displayed if the rain amount is greater than 0, resides in the presenter. Thus, it is really important to add tests to this logic, and unit tests are easy and fast to write and run.
Go ahead and click the play triangle next to testOnViewCreatedFlow()
and select Run testOnViewCreatedFlow() to see your unit test for the presenter run and pass.
Note, the final project also contains an instrumented test file named MainActivityTest.kt, in the androidTest package. The weatherIconIsLoadedInImageView()
test verifies that the icon is correctly displayed in the view. With an instrumented UI test, you can also test your view components, but they must be run on an Android emulator or device, and so do not run as fast as unit tests.
Pitfalls of MVP
Unfortunately, it’s not all sunshine and rainbows with MVP. Now that you’ve seen how MVP architecture in practice on the Android platform, you’re in a better place to understand some of its shortcomings.
View Lifecycle
At any given time, the activity and any associated fragments (the views) may be destroyed. Since the presenter keeps a reference to the view, you may try to update an activity that is already detached.
In your sample app, you added a method named onDestroy()
to BasePresenter
. When the activity lifecycle method onDestroy()
gets called on MainActivity
, you call MainPresenter::onDestroy()
, which sets the presenter reference to view
to null
. Kotlin null safety features prevent you from calling a method on a null
view.
Presenter Code Reuse
Most of the logic will exist in the presenters of your app. On a screen with many interactions, the presenter can become quite large. Creating BasePresenters and utility functions are the easiest path to code reuse. The presenter can only attach with a specific view. Any code inside may not be reusable and neither can the presenter. If you have an app that may reuse the same view it would be difficult to have a common presenter between them.
You can write BasePresenters and implement functions in there to be common between all presenters in order to help with reuse. Much of this will be logging code and lifecycle code, things best handled with utility functions.
Presenter State
The presenter also becomes dependent on its state. Some methods may need to check on the current state of the presenter in order to function. Data flows in two directions, to the model and to the view. The presenter behaves like a state machine but does not actually have the functionality of one, which can lead to awkwardness when trying to figure out these states.
Where to Go From Here?
This tutorial was an overview of using a simple, yet effective, pattern to make your code much easier to work with. MVP has uses beyond mobile programming and is also used in desktop and web applications. The idea of separating your concerns is a powerful one. Consider your needs and explore some other ways to help keep your code modular and testable.
You can download the final project using the Download Materials button at the top or bottom of this page.
You might also be surprised to hear that there are other patterns, such as Model-View-View-Model (MVVM) and Model-View-Intent (MVI) for you to explore. Discover other Common Design Patterns for Android with Kotlin to make your Android code cleaner and easier to understand with these common design patterns for Android apps.
To dive deeper into MVP and MVVM, check out our MVP on Android and MVVM on Android video courses.
Also, checkout Android Architecture Blueprints. A collection of samples to discuss and showcase different architectural tools and patterns for Android apps.
Here are a few additional links that also may help with your implementation of MVP:
MVP by Florina Muntenescu – Android Developer Advocate @Google
Simple Explanation and Debate @ StackOverflow
Android Architectures @ Codepath
I hope you enjoyed this getting started tutorial on Model-View-Presenter! If you have any questions or comments, please join the forum discussion below.