This article covers how I utilized MVP in the architecture of the DebugRank app. First I’ll cover what MVP is at a high level, then I’ll provide some code snippets of what each layer looks like in Android. Afterwards I’ll cover the use cases, who, what, and when to use MVP, and finally some Q&A. You can find out more about the DebugRank app from the DebugRank blog. Throughout this blog I'll be providing gist code snippets, but you can find the full DebugRank app on github at: https://github.com/grapecity/DebugRank. If you are interested in trying it out you can download the app from Google Play.
Model View Presenter (MVP) is the latest and greatest Android architecture pattern that helps you decouple business logic (Model) from view logic (Activity / Fragment) by utilizing an intermediate step called the Presenter.
The view is extremely limited in MVP, it does nothing but display data and navigate to a new screen when the presenter tells it to. The View has no visibility of the Model, except for the POJOs / Entities. In regards to Android specifically, this would include my Activities, Fragments, RecyclerView Adapters, and anything that extends the Android View class. My personal preference is to only let the Activities & Fragments talk to the Presenters and leave the Views & Adapters to only display data and delegate events (OnClick) back to the Activity or Fragment.
The Presenter sits between the View and the Model, and it reacts to events passed from the View. For instance, when the SaveBtn is clicked inside my view, I would call presenter.save(). Once this occurs, the presenter utilizes the Model to determine if all criteria is met (I.e. a valid email address) and, if so, we can safely save the data. The presenter would then either notify the view to display an error message, or notify the view to navigate.
The Model includes business logic that is entirely decoupled from the UI / Platform specific logic. This encompasses my Entities, backend services / helpers (web, database, etc), and business logic. I will have a wrapper class (either called Model or Interactor) that will talk directly to the backend services and hold business logic. These backend services ideally should be injected using Dagger 2, I will cover this in a future blog.
The first screen in the DebugRank app is the languages view, where the user is presented with a list of programming languages, and, upon click of a language, it navigates to the next screen. This view's only interaction with the presenter is languagesPresenter.attachView and languagesPresenter.programmingLanguageClicked.
Is this gist not enough? Find the full View implementation on github at: ILanguagesView LanguagesActivity LanguagesAdapter
The presenter delegates loading of the languages to the interactor (model). Once loaded it will simply pass the list of languages back to the view. The presenter also determines what action to take when a programming language is clicked. For this example it directly tells the view to navigate to the next screen. More advanced samples might then call the interactor (model) to do some data validation before navigating. Note that the Presenter is platform independent and should live in a java module.
Is this gist not enough? Find the full Presenter implementation on github at: ILanguagesPresenter LanguagesPresenterImpl
The model / interactor will either contain business logic itself (I.e. data validation) or will delegate REST / DB request to another backend service / repository. Note that the interactor has no knowledge of the View or the Presenter, technically logic in the Model / Interactor could be application independent and shared across multiple apps. Also note that the Model / Interactor is platform independent and should live in a java module.
Is this gist not enough? Find the full Model / Interactor implementation on github at: ILangugagesInteractor LanguagesInteractorImpl
MVP is not a silver bullet. It's important to analyze a project's requirements and resources to help determine what architecture is appropriate. If you or your organization values testability then I'd strongly suggest considering MVP.
MVP is not for you. You won't be able to appreciate what MVP solves until you've experienced writing an Android app without it. Additionally, you'll spend more time wondering what logic goes where than the actual writing of said logic. Once you accumulate some more experience with the platform it’s worth revisiting the topic since it can potentially improve your programming skills and apps.
There is where MVP shines. Enterprise customers have a higher standard of quality than any other customer base. Bugs will make or break the app. Using MVP will help you have unit tests for every piece of logic and ultimately reduce the bug count.
Depending on the complexity of the app and size of your team will help you determine if MVP is a good fit. The larger the project or team will allow you to reap more benefits compared to smaller apps / teams.
Build / test speed & code reuse. It takes much less time to build and test a java module then it does an android module. Ideally the java module would be built independently of the android app and consumed as an aar to help increase build speeds even more. Additionally it enables multiple platforms possible. The actual relevant logic sits in a java module and multiple platforms / UIs can consume it (android, java applet, web, etc).
Everything has an interface to improve testability. I can easily test individual layers by mocking dependencies, this will be covered in a future blog utilizing Mockito.
For me personally, the Model represents all business logic decoupled from the UI. The interactor specifically are use cases for an app in particular. In this context an app is something like "Facebook" where the UI is the individual platforms (Android, iOS, etc). This may also be known as CLEAN architecture and will be covered in a future blog.
Want the full code of DebugRank to get a better understanding of MVP? See: https://github.com/grapecity/DebugRank