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.

Leave a rating/review
Download materials
Save for later
Share

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 ModelViewPresenter. 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 modelviewpresenter 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.

Screenshot of MVP sample app

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:

Android Sample App - Model View Controller

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:

MVP 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! :]

Refactoring Umbrella

You are going to transition the Umbrella app to an MVP architecture. Here’s a diagram of the components that you will add to the project, and how each component will interact with each other.

Android Sample App - Model View Presenter

Organizing Features

One popular way of managing the parts of an app is to organize them by feature. A feature is composed of the model, the views, the presenters, as well as dependency injection (DI) code to create and provide each component. This way, you can add and remove features from your app as a module.

Your app has only one feature: the main screen. You are going to implement MVP for the main screen, and you will create some components prefixed with Main.

Adding Dependency Injection

In this section, you will create a handmade dependency injector.

To start, create an interface named DependencyInjector in the root com.raywenderlich.android.rwandroidtutorial/ package.

interface DependencyInjector {
​  fun weatherRepository() : WeatherRepository
}

Next, create a class DependencyInjectorImpl in the same package that implements the interface.

class DependencyInjectorImpl : DependencyInjector {
  override fun weatherRepository() : WeatherRepository {
​    return WeatherRepositoryImpl()
  }
}

There’s no strict reason here for why we split this dependency injector class into an interface and an implementation class, however, it’s just considered good practice in case you ever wanted to swap in a different implementation in the future.

Note: In a production app, you will choose among any of the DI frameworks in the Android ecosystem (e.g., Dagger2), which will help in working with an MVP-architected app.

To learn more, read the tutorials Dependency Injection in Android with Dagger 2 and Kotlin or Dependency Injection with Koin.

Defining the Contract

We also have interfaces to define the presenter and the view. Interfaces help with decoupling the parts of the app. The interface forms a contract between the presenter and view.

First, create a new file named BasePresenter.kt in the same base package you’ve been working in, and add the following code:

interface BasePresenter {
​  fun onDestroy()
}

This is a generic interface that any presenter you add to your project should implement. It contains a single method named onDestroy() that basically acts as a facade for the Android lifecycle callback.

Also, create a new file named BaseView.kt, and add the following code:

interface BaseView<T> {
  fun setPresenter(presenter : T)
}

Similar to BasePresenter, this is the interface that all views in your app should implement. Since all views interact with a presenter, the view is given a generic type T for the presenter, and they must all contain a setPresenter() method.

Next, create a contract interface named MainContract, which defines interfaces for the view and presenter for the Main screen, and update it to look as follows:

interface MainContract {
  interface Presenter : BasePresenter {
​    fun onViewCreated()
​    fun onLoadWeatherTapped()
  }

  interface View : BaseView<Presenter> {
​    fun displayWeatherState(weatherState: WeatherState)
  }
}

Notice here that you’re creating interfaces for the specific activity, and that they inherit from the the base interfaces we previously defined. You can see that MainContract.Presenter is interested in being called back by the MainContract.View when the view is created through onViewCreated() and when the user taps on the “Load Weather” button through onLoadWeatherTapped(). Similarly, the view can be invoked to display weather information through displayWeatherState(), which is only called by the presenter.

Defining the Presenter

You have your interfaces in place. Now it’s a matter of assigning these responsibilities to the proper class. First, create a new file named MainPresenter.kt, and set it up as follows:

// 1
class MainPresenter(view: MainContract.View,
                    dependencyInjector: DependencyInjector)
  : MainContract.Presenter {
  // 2
  private val weatherRepository: WeatherRepository
      = dependencyInjector.weatherRepository()

  // 3
  private var view: MainContract.View? = view
}

Taking this code in pieces:

  1. The presenter constructor takes in an instance of the view, along with the dependency injector created earlier, which it uses to get an instance of the model.
  2. The presenter holds on to an instance of the WeatherRepository, which in this app is the model.
  3. The presenter also holds on to a reference to the view; however, note that it interacts with the interface only, as defined in MainContract.

Next, move two private methods from MainActivity into the presenter.

private fun loadWeather() {
  val weather = weatherRepository.loadWeather()
  val weatherState = weatherStateForWeather(weather)

  // Make sure to call the displayWeatherState on the view
  view?.displayWeatherState(weatherState)
}

private fun weatherStateForWeather(weather: Weather) : WeatherState {
​  if (weather.rain!!.amount!! > 0) {
​    return WeatherState.RAIN
​  }
​  return WeatherState.SUN
}

There’s nothing remarkable about these methods, however, be sure to forward the call to displayWeatherState() in loadWeather() to your view object:

view?.displayWeatherState(weatherState)  

Finally, implement the rest of the presenter contract by adding the following methods:

override fun onDestroy() {
  this.view = null
}

override fun onViewCreated() {
​  loadWeather()
}

override fun onLoadWeatherTapped() {
​  loadWeather()
}

Here, you do some clean up in onDestroy() and invoke fetching the weather data in both onViewCreated() and onLoadWeatherTapped().

An important point to notice is that the presenter has no code that uses the Android APIs.

Writing the View

Now, replace the content of MainActivity.kt with the following:

package com.raywenderlich.android.rwandroidtutorial

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.Button
import android.widget.ImageView

// 1
class MainActivity : AppCompatActivity(), MainContract.View {
  internal lateinit var imageView: ImageView
  internal lateinit var button: Button

  // 2
  internal lateinit var presenter: MainContract.Presenter

  override fun onCreate(savedInstanceState: Bundle?) {
​    super.onCreate(savedInstanceState)
​    setContentView(R.layout.activity_main)

    imageView = findViewById(R.id.imageView)
    button = findViewById(R.id.button)
    
    // 3
    setPresenter(MainPresenter(this, DependencyInjectorImpl()))
    presenter.onViewCreated()
    
    // 4
    button.setOnClickListener { presenter.onLoadWeatherTapped() }
  }

  // 5
  override fun onDestroy() {
​    presenter.onDestroy()
​    super.onDestroy()
  }

  // 6
  override fun setPresenter(presenter: MainContract.Presenter) {
​    this.presenter = presenter
  }

  // 7
  override fun displayWeatherState(weatherState: WeatherState) {
​    val drawable = resources.getDrawable(weatherDrawableResId(weatherState),
​            applicationContext.getTheme())
​    this.imageView.setImageDrawable(drawable)
  }

  fun weatherDrawableResId(weatherState: WeatherState) : Int {
​    return when (weatherState) {
​      WeatherState.SUN -> R.drawable.ic_sun
​      WeatherState.RAIN -> R.drawable.ic_umbrella
​    }
  }
}

Let’s highlight the most important changes made to MainActivity:

  1. Implement the MainContract.View interface. This jives well with our expectations of views.
  2. Add a presenter property instead of the model weatherRepository. As was previously mentioned, the view needs the presenter to invoke user initiated callbacks.
  3. Store a reference to the presenter just after creating it. Notice that it also creates and passes an instance of DependencyInjectorImpl as part of the creation.
  4. Offload handling of the button callback to the presenter.
  5. Notify the presenter when the view is being destroyed. Recall that the presenter uses this opportunity to clean up any state that is no longer required beyond this point.
  6. Implement the method required from the BaseView interface to set the presenter.
  7. Add override to the displayWeatherState() method, since it is now part of the view interface.

Build and run the app just to make sure it all still works.

Screenshot of MVP sample app

Based on the refactoring you’ve done to MainActivity, it should be clear how the app data flows and how the plumbing is set up between the model, the view and the presenter.

Congratulations! You’ve MVP-ified your app with a different architecture. You’ve managed to extract all the business logic to a place where it can now be easily tested with lightweight unit tests. You’ll tackle that in the next section.

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.