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?
A clean codebase is always a pleasure to work with. A well organized codebase is easy to maintain, is robust, performs well, is testable and is self-documenting. Picking an architecture for Android can be tricky, however in this tutorial, you will look at one way to achieve a clean codebase using MVP, short for Model–View–Presenter. You will also learn how this pattern fits within the Android ecosystem.
In this tutorial, you will build an app named Umbrella which will show an umbrella icon when there’s rain in the forecast, and a sun icon when it’s going to be nice and sunny outside. The important thing here is, you will refactor an initial version of this app, so that it leverages the use of the model – view – presenter pattern. You will also learn how the refactored components of the app can be tested.
Getting Started
Download and unzip the materials for this tutorial using the Download Materials button at the top or bottom of this page. Open the starter project in Android Studio 3.2.1 or greater, then build and run to see the app you’ll be working with.
Nice right? :] If you tap the Load Weather button, the app will randomly change the weather between sunny and raining. Most of the logic is in a single class — MainActivity.kt
. Familiarize yourself with the starter project code, which uses a structure as illustrated in the following diagram:
The weather data comes from the OpenWeatherMap API. In a production app, you would fetch this data with a networking library such as Retrofit.
To keep this tutorial simple, there is a class named WeatherRepositoryImpl
, which inflates a model object named Weather
from a JSON payload using Moshi; a JSON library for Android and Java, which makes it easy to parse JSON into Java objects.
The flow of the application is simple. When the view finishes loading, you fetch the weather data. Then, you check to see if the rain value is a positive one, and then you display the umbrella image or the sun image. But you’re not here for a tutorial on UI, you’re here about architecture! First, you’ll explore what is considered the traditional approach to software architecture for many systems.
MVC: The Traditional Approach
A traditionally written Android app is made up of three main components.
- Models: Models contain the displayed data. Usually, this data is fetched from the network or a local database. Then the data is put into small, simple classes which the other components can use.
- Views: Views are what’s displayed to the user. They also handle any interaction a user may have with the screen — click listeners, for example. A view should only be responsible for displaying things and should not contain any business logic. As such, the view tends to be a lightweight component compared to a controller and typically doesn’t contain much code. In Android, the view responsibility often falls onto activities and fragments.
- Controllers: Controllers are a way to connect models and views together. The controller updates the model when something happens in the view. The controller will also update the view when the model changes. Too often, the controller responsibilities are also found in activities and fragments.
This architecture is most commonly known as MVC. Although this acronym tends to be synonymous with being bad, it is an architecture that has served well in the absence of any architecture. However, it does have its flaws.
What Is Wrong With This Approach?
When it comes to implementing MVC on the Android platform, things get tricky. To start, most of the logic ends up in the controller. It is a common Android problem to have a controller activity that contains all the logic. The controller then has all the responsibility for what’s displayed on screen. For a simple screen, this can be manageable but, as features get added, this file will keep growing.
Moreover, Android activities are tightly-coupled to both UI and data access mechanisms. Thus, it is easy to fall into the trap of placing both controller and view logic in an activity or fragment. This, however, creates tightly-coupled components which makes it harder to refactor and change.
The view layer should only concern itself with views. It shouldn’t have to know about databases or network calls. Moreover, it is difficult to test components when they are tightly-coupled. You should test the code that exists in the controller. Much of the code under test will end up needing to run Android SDK components, such as activties, which need a full running device or emulator. Basic unit testing will not help; you will need to use expensive instrumented unit tests to test the basic parts of the app.
MVP: An Alternative Approach
One alternative to the problems presented by MVC is to decouple some of the parts from each other. MVP is an architecture pattern that you can use to deal with some of the shortcomings of MVC, and is a good alternative architecture. It provides an easy way to think about the structure of your app. It provides modularity, testability and, in general, a more clean and maintainable codebase.
Picking apart the acronym, MVP is composed of the following components:
- Model: The model will continue to contain the data in simple classes, so nothing really changes here.
- View: The view will continue to be implemented using Activity or Fragment classes, but we will change the scope of what the view controls.
- Presenter: The last part is the presenter, which handles UI updates based on changes to the data model, and also processes users inputs. The presenter will contain much of the business code and replaces the controller from MVC.
In MVP, instead of having a controller Activity class which handles both changes to the model and what’s displayed on screen, the controller and view parts are separated out, and the both the presenter and view become more lightweight.
Data (model) and UI (view), only communicate with one another through an intermediary (the presenter) . The presenter contains the bulk of the business logic, while the view focuses on how to display the data. The controller responsibility is now split between the view and presenter. A presenter handles the flow of data, and abstracts away the business logic from the controller. The Android-specific code stays in the view layer, and the presenter can be tested independently from the Android SDK.
So how does data flow between these components? Take a look at this diagram:
In the code, you’ll see interfaces which define the presenter and the view. Interfaces help with decoupling the parts of the architecture. The interface forms a contract between the presenter and view. They will also help you define and write test cases later.
When there are changes to the data, the presenter is notified that the data has changed. The view will then receive that data through the presenter and update itself using the data from the presenter. You can also go in the opposite direction with events from the view. When a user interacts with the view, a method on the presenter is invoked. The presenter then calls the appropriate method for that action to update the model.
Ok, enough chatter, time to refactor! :]