• MVP for Android: How to organize the Presentation Layer
  • Antonio Leiva
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: Moosphon
  • Proofread: github.com/gs666, github.com/Qiuk17

The MVP (Model View Presenter) pattern is a derivative of the famous MVC (Model View Controller) pattern and is one of the most popular patterns for managing the presentation layer in Android applications.

The article was first published in April 2014 and has been popular ever since. So I decided to update it to address most of the concerns people had and convert the code to Kotlin.

Architectural patterns have changed significantly since then, such as MVVM with architectural components, but MVP is still valid and an option worth considering.

What is MVP mode?

The MVP model separates the Presenter layer from the logic, and in doing so, separates everything about how the UI works from how we represent it on screen. Ideally, MVP mode would implement the same logic possibly with a completely different and interchangeable interface.

The first thing to make clear is that the MVP itself is not an architecture, it is only responsible for the presentation layer. This is a controversial statement, so I want to explain it in more depth.

You may find that MVP is defined as an architectural pattern because it can become part of your application architecture. But you shouldn’t think of it that way, because when you take away the MVP, your architecture is still intact. MVP only shapes the presentation layer, but if you want flexible and extensible applications, the rest of the layers still need a good architecture.

An example of a complete architectural system could be Clean Architecture, but there are many other options.

In any case, it’s always good to use MVP in a structure where you’ve never used it before.

Why MVP?

In Android development, we run into a serious problem: Activities are highly coupled to the user interface and data access mechanisms. We can find extreme examples such as CursorAdapter, which mixes the Adapter that is part of the view layer with the Cursor that is part of the data access layer.

To be able to easily scale and maintain an application, we need to use architectures that can be separated from each other. If instead of getting data from a database, I get it from a Web server, what do I do next? We might have to rewrite the entire view layer.

MVP makes the view independent of our data source. We need to divide the application into at least three different layers so that we can test them independently. With MVP, we can remove most of the business logic processing from the Activity so that we can Test it without Instrumentation tests.

How to implement MVP in Android?

Well, this is where it starts to split. There are many variations of the MVP, and everyone can adjust the mode according to their needs and the way they feel more comfortable. It depends on the number of tasks we delegate to presenters.

Should the View layer be responsible for enabling or disabling a progress bar, or should the Presenter be responsible? And who should decide what Action Bar should do? This is where the hard decisions begin. I’ll show you how I handle this situation in general, but I hope this article is more of a place for discussion than a strict constraint on how MVP should be applied, because there is no “standard” way to implement it.

For this article, I’ve implemented a very simple example, which you can find on my Github login page and home page. For simplicity, the code in this article is implemented using Kotlin, but you can also view code written in Java 8 in the repository.

Model layer

In an application with a fully layered architecture, the Model here is simply a gateway to the domain layer or business logic layer. If we use Uncle Bob’s Clean Architecture, the Model here might be an Interactor that implements a use case. But for the purposes of this article, it is sufficient to think of the Model as a provider that displays data to the View layer.

If you examine the code, you’ll see that I created two interactors with artificial delay operations to simulate requests to the server. One of the Interactor constructs:

class LoginInteractor {

    ...

    fun login(username: String, password: String, listener: OnLoginFinishedListener) {
        // Mock login. I'm creating a handler to delay the answer a couple of seconds postDelayed(2000) { when { username.isEmpty() -> listener.onUsernameError() password.isEmpty() -> listener.onPasswordError() else -> listener.onSuccess() } } } }Copy the code

This is a simple method that takes a username and password and does some validation.

The View layer

The View layer is usually composed of an Activity (or a Fragment, a View, depending on the structure of the App) that contains a reference to a Presenter. Ideally, a Presenter is provided via dependency injection (e.g., Dagger), but you can also create a Presenter object directly if you don’t use such a tool. The only thing the View needs to do is call the corresponding method in Presenter whenever a user action occurs (such as a button being clicked).

Since a View must be independent of the Presenter layer, it needs to implement an interface. Here are the interfaces used in the example:

interface LoginView {
    fun showProgress()
    fun hideProgress()
    fun setUsernameError()
    fun setPasswordError()
    fun navigateToHome()
}
Copy the code

Interfaces have effective ways to show or hide progress bars, display error messages, jump to the next page, and so on. As mentioned above, there are many ways to implement these features, but I prefer to list the simplest and most intuitive way.

The Activity can then implement these methods. Here I show you a few to give you an idea of how to use them:

class LoginActivity : AppCompatActivity(), LoginView {
    ...

    override fun showProgress() {
        progress.visibility = View.VISIBLE
    }

    override fun hideProgress() {
        progress.visibility = View.GONE
    }

    override fun setUsernameError() {
        username.error = getString(R.string.username_error)
    }
}
Copy the code

But if you remember, I also told you that the View layer uses Presenter to notify users of interactions. Here’s how it’s used:

class LoginActivity : AppCompatActivity(), LoginView {

    private val presenter = LoginPresenter(this, LoginInteractor())

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

        button.setOnClickListener { validateCredentials() }
    }

    private fun validateCredentials() {
        presenter.validateCredentials(username.text.toString(), password.text.toString())
    }

    override fun onDestroy() {
        presenter.onDestroy()
        super.onDestroy()
    }
    ...
}
Copy the code

Presenter is defined as a property of the Activity, and when the button is clicked, it calls the validateCredentials() method, which will notify Presenter.

The same is true for the onDestroy() method. We’ll see why a Presenter needs to be notified in this case later.

Presenter layer

Presenter acts as the middleman between the View layer and the Model layer. It gets the receipt from the Model layer and returns the formatted data to the View layer.

Also, unlike the typical MVC pattern, Presenter decides what to do when you interact with the View layer. Therefore, it will provide a method for each action that the user can perform. We see it in the View layer, and here’s the code:

class LoginPresenter(var loginView: LoginView? , val loginInteractor: LoginInteractor) : LoginInteractor.OnLoginFinishedListener { fun validateCredentials(username: String, password: String) { loginView? .showProgress() loginInteractor.login(username, password, this) } ... }Copy the code

There are some risks to the MVP model, but the most important problem that is often overlooked is that presenters are permanently attached to views. And the View layer is usually an Activity, which means:

  • We might leak activities due to long running tasks
  • We might update the view if the Activity has been destroyed

First of all, I wouldn’t be too worried if you could guarantee that you could finish your background tasks in a reasonable amount of time. Leaking your Activity for 5-10 seconds can make your App look bad, and the solution is often complicated.

The second is more worrying. Imagine that it takes you 10 seconds to send a request to the server, but the user shuts down the Activity five seconds later. When the callback method is being called and the UI is being updated, the App will crash because the Activity is being destroyed.

To solve this problem, we can call onDestroy() in the Activity and clear the View:

fun onDestroy() {
    loginView = null
}
Copy the code

This way we can avoid invoking the Activity when the task end time is inconsistent with the Activity destruction time.

conclusion

It’s not easy to separate the user interface layer from the logical layer on Android, but MVP mode makes it much easier to prevent our activities from ending up in highly coupled classes with hundreds or thousands of lines of code. In large application development, good code management is essential. Otherwise, it becomes difficult to maintain and extend the code.

Today, there are other alternatives like MVVM, and I will write a new article comparing MVVM to MVP and helping developers migrate. So stay tuned to my blog!

Keep this repository in mind and you can see MVP code examples in Kotlin and Java here.

If you want to learn more about Kotlin, check out the Sample app in my Kotlin for Android Developers book, or watch the online courses.

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.