A lot of choices are available when it comes to Mobile App architectural design. The top three are, without doubt, Model-View-Controller also known as MVC, Model-View-ViewModel (MVVM) and Model-View-Presenter (MVP).
All of them have their variations in terms of the actual implementation such as how the components are tied to each other, how the communication between the components is done, and also how the input from the user is taken and processed.
Here is where I want to break down MVP and talk about a specific way to implement it. In my experience, this is the most comprehensive and highly decoupled way to implement MVP within an Android Application.
Just to clarify the examples and unify the code style, the bus events library used is EventBus by Green Robot. If you want to dig deeper into this MVP implementation, read this article I wrote about how to use the library I implemented.
Bus: A way of communication between different components.
Bus Events: Events are messages (objects) triggered by any component that needs to notify some listener(s) that something happened (click on a widget, text changed, pinch, swipe, shake, etc). Events are sent through the bus.
Event subscribers: Events sent to the bus, need to be handled by another component named subscriber. The subscriber can be any class, almost always the subscriber is no other than the Presenter.
Presenter: The Presenter is in charge of the the orchestration between the Model and the View. It basically receive events from both and act consequently. The Presenter is the only component that knows others. It has a reference to the View and a another to the Model.
View: The View is the component that will hold the widgets and will catch every user input but rather than process it in any way, it will forward it to the Presenter by posting an event.
Model: The Model is roughly the data and business-logic layer. It will be in charge of any heavy operation such as server calls, database queries, save shared preferences, etc. If the Model needs to notify anything to the Presenter, it will be done, again, by posting an event.
Here is a figure that illustrates the components and their relationships.
As you can see, there is no hard wiring between on View-Presenter and Model-Presenter but instead, the communication is achieved using events.
You may think of events as if they were messages being broadcasted onto a shared channel. The idea is that the sender (poster) does not have to know the recipient (subscriber), in fact, it doesn’t. This is a non-blocking operation, it means that if there is no active subscriber for the event being posted, nothing happens.
In order to send a message to a component, the sender has to post it on the bus according to the following schema:
Here the bus can be either a reference held by the sender, or a new instance obtained at runtime.
The class SomeKindOfEvent is typically a public and static class declared within the class that posts the event. The parameters that the constructor received are optional depending on the necessities. As convention, the attributes declared in SomeKindOfEvent are public and final, so there is no need to write getters and setters.
If an event is shared between multiple senders, the convention is to declare them into their own class files under a dedicated package.
In order to listen or receive posted messages, someone has to Subscribe to the event on the bus. This is achieved by simply annotating a method with @Subscribe and making sure that the method receives exactly one parameter of the same type of the event posted as follows
Generally speaking, events are posted by either the View, the Model, an Adapter, or any other component that needs to notify someone that something happened. Once again, in general, the only component that subscribes to those events is the Presenter.
The Presenter, in order to get subscribed to the events on the bus, needs to get registered on it. One way to do it is to have a reference to the Presenter in the Activity/Fragment/Dialog that created it, then register it inside the onResume() method, and then unregister it inside the onPause(). The registration and unregistration is done inside onResume() and onPause() methods in order to ignore any event that is posted while the Activity/Fragment/Dialog is not visible, and therefore avoid unnecessary work.
Let’s assume that we have an Activity called MainActivity, and we want to create a MVP architecture for it. Our Activity and its MVP would look like this:
The View has to keep a reference to the Activity in order to get the Context if needed. Notice that we are not holding the reference by saving the instance in an attribute but instead we are creating a WeakReference object that will hold the value, this is because we don’t want to prevent the garbage collector to mark our Activity as eligible to collect if we are not using it.
If the Presenter needs, for example, to start a new Activity, it will need the current Activity to do it. The Presenter can simply call the getActivity() method on the View and then check if it is not null, this is necessary because the getActivity() might be called after an asynchronous call and hence, the Activity may not be visible anymore.
Sample of View notifying Presenter
Sample of Model notifying Presenter
Low coupling: Mainly because of events, the Application components are decoupled and hence, its extensibility and maintainability is increased.
High cohesion: As the Model is in charge of the heavy operations, server calls, database queries among others, the View only holds widgets and forward any action to the Presenter, and the Presenter is in charge of the orchestration, this means that, if the input needs further processing, it will be forwarded to the Model; components have high cohesion and specific responsibilities.
Unit testing: Testing is made very simple while using MVP because you only write tests for Model and Presenter (and of course for any other class that you use other than the View). You don’t need to write unit tests for the View class since it should not have any logic inside and therefore, there is no reason to unit-test it. We rely on the Android SDK team that the widgets work properly :).
Maintainability: Since every component has a specific role in this architecture, you can rest assure that if you have a bug/feature/change, you can quickly identify the class to work on.
For instance, let’s say that you build an App that displays come customers list and your client now wants to display that customers list but instead of sorting it by name, he wants to sort it by phone number. This is clearly a job for the Model, so you can quickly jump to CustomersModel and start working.
If prior to start SomeActivity you now need to start LoginActivity, you can tell that the change needs to be done inside the Presenter.
And of course, if you need to change/add/remove any widget listener such as OnClickListener, OnTouchListener, OnTextChanged, etc. You know that it needs to be done on the View.
As MVP aims to separate and distribute the responsibilities of its components, it provides and nice and clean way to approach any kind of Applications.
We all know a friend of a friend that has an application in which there is a Thousand-liner-Activity a.k.a. The Unmaintainable where all business logic is inside the Activity because he made copy-paste-coding from Stack Overflow.
Bus events provide a good way to communicate between components, they let you link one with another without having to hold references or anything, but, use them wisely. Over-using events can exponentially decrease code clearness and may quickly lead to spaghetti code.
Model-View-Presenter architecture lets you build clean code while increasing your productivity in the long term, not only because you can split work between co-workers, but also because you can improve your time-to-find-a-bug while dealing with defects and that is, trust me, very satisfying.
Testing, testing and more testing. MVP allows you to easily write separated tests for the Model and the Presenter. Since the Model does not have any reference to the View or the Presenter, it can be tested separately, allowing the developer to abstract the Model from any other component such as databases.
The Presenter can be unit-tested by mocking the View and the Model, this gives the developer the ability to simulate different results in order to test different behaviours of the Presenter.
I’d recommend using Mockito to represent objects while writing unit tests.
If you have questions or comments, please leave them below or write me at firstname.lastname@example.org and I will answer as fast as I can.