Android MVP basics with sample app
--
There are two de-facto patterns for architecting an Android app — MVP (Model-View-Presenter) and MVVM (Model-View-ViewModel). My article focuses on MVP as it is more mature, since it has been around for a while. There are many articles on how to architect an Android app using MVP pattern. My article uses them as a reference to provide basic understanding of the MVP pattern with a sample app.
Why architect anything?
It is always a good practice to think about code architecture. An optimal architecture pays attention to the following core principles:-
- Single Responsibility Principle: As the name suggests, a component , such as a
class
, should be responsible for only one single task. This makes the code more maintainable and hence readable. - Dependency Injection Principle: A fancy term that simply means a component, such as as a
class
, should not seek or create any external components (dependencies) on its own and must rely on its container to provide it with its dependencies. This decoupling makes the code more testable and extensible.
Android as a system doesn’t enforce the above two core architectural principles. It doesn’t enforce decoupling of the UI component, such as an Activity
, and the corresponding business logic that populates the UI component with data. This means you can end up with a problematic Activity
, like the one below, that is difficult to test and manage!
The fact that Android does not enforce the above architectural principles, does not make it flawed! Android simply delegates the task of architecting the apps well to the app creator.
How do I architect?
This is where MVP architectural pattern shines. MVP enforces both the Single Responsibility Principle and Dependency Injection Principle.
MVP stands for Model, View and Presenter. I am going to explain this pattern with the help of a sample app I created.
The sample app fetches and displays the interesting photos and their comments for the most recent day from the Flickr API
The Model
- The Model is a layer that consists of components responsible for functionalities such as fetching, generating, storing and exposing data.
- These functionalities should be performed on the background thread as they can be time consuming and hence can potentially block the main UI thread.
- Any data fetched or generated in the Model layer is usually returned to the
Presenter
via callbacks.
In the context of the sample app, these functionalities are assigned to relevant components as follows:-
Fetching/Generating data:-
RemoteDataSource
: theclass
for fetching data from Flickr API on a background thread and returning data via callbacks on the main UI thread.LocalDataSource
: theclass
for fetching data from a local SQLite DB on a background thread and returning data via callbacks on the main UI thread.
Storing data:-
LocalDataSource
: theclass
for storing data into a local SQLite DB on a background thread.Photo
: aclass
to store photo data.Comment
: aclass
to store comment data.
Exposing data:-
DataSource
: theinterface
that exposes fetching and storing data through helper methods. This is to be implemented by all data sources such as the aboveRemoteDataSource
andLocalDataSource
.DataRepository
: the primaryclass
for any Presenter to interact with for fetching and storing data. It is the middleman in front of all data sources such asRemoteDataSource
andLocalDataSource
and delegates the work to them depending on conditions such as network availability, etc.
The View
- The View is a component responsible for displaying data given to it from its
Presenter
and passing user input to itsPresenter
. - Each View should ideally have a single
Presenter
. - The View should use its
Presenter
’s interface to communicate with itsPresenter
so that it is decoupled from any specific implementation of itsPresenter
and can be tested in isolation. - The View, ideally, should be lean and have no business logic. It should get data only via its
Presenter
and not directly from theModel
layer. - In relation to Android, the View can be a
Activity
,Fragment
orAndroid View
. - The View should notify its
Presenter
about its background and foreground lifecycle so that itsPresenter
can reference/unreference the View properly to prevent memory leaks and rendering exceptions. ForActivity
orFragment
, thePresenter
is notified usually from theironResume()
andonPause()
methods.
In the context of the sample app, the following are the relevant View components:-
MainActivity
: the containerclass
responsible for rendering the relevantFragment
and managing global state. This is based on the Fragment Oriented Architecture (FOA) which is outside the scope of this blog. One benefit of usingFragment
is that it can easily preserve state on configuration changes such as screen orientation. I do plan on writing a post about FOA in the future. Happy to discuss more about it on the side :)PhotoFragment
: theFragment
that receives photo data from itsPhotoContract.Presenter
, which is implemented by thePhotosPresenter
, and renders a list of photos and also handles user actions, such as clicks on photos, and passes it to thePhotoContract.Presenter
.PhotosRecyclerAdapter
: theAdapter
that renders and populates each photo in the photos list.PhotoContract.View
: theinterface
that exposes functionalities of the view,PhotoFragment
, to be used by the view’sPresenter
.PhotoDetailFragment
: theFragment
that receives photo data from thePhotosFragment
on its creation and comment data from itsPhotoContract.Presenter
. It eventually renders the photo and its list of comments.CommentsRecyclerAdapter
: theAdapter
that renders and populates each comment in the comments list.PhotoDetailContract.View
: theinterface
that exposes functionalities of the view,PhotoDetailFragment
, to be used by the views’sPresenter
.
The Presenter
- The Presenter acts as a controller and sits in the middle between the
View
and the Model layer. - Each Presenter should ideally be paired with a single
View
. - The Presenter should use its
View
’s interface to communicate with itsView
so that it is decoupled from any specific implementation of itsView
and can be tested in isolation. - The Presenter is responsible for calling the
Model
layer for data usually at the request of itsView
and receive that data via callbacks. - The Presenter calls relevant methods in its
View
to display data. - The Presenter handles any additional business logic and data processing that needs to be done before displaying the data. Any time consuming processing should be done on a background thread.
- The Presenter, ideally, should only contain standard java code and specifically not contain any Android framework code so that it can be reused for other types of views and can be tested easily without an Android device.
- The Presenter should also be notified about its
View
lifecycle such as whether theView
is in foreground or background, so that the Presenter can reference/unreference itsView
. This helps to prevent memory leaks by allowing theView
to be garbage collected. In relation toActivity
orFragment
, the Presenter can be notified about their lifecycle from theironResume()
andonPause()
methods. The Presenter should also check if itsView
is referenced and notnull
before calling anyView
method to prevent any Exception.
In the context of the sample app, the following are the relevant Presenter components:-
PhotosPresenter
: thePresenter
responsible forPhotosContract.View
which is implemented byPhotosFragment
. It fetches photo data by callingDataRepository
at the request of itsPhotoContract.View
and delivers that data back to itsPhotosContract.View
. The Presenter also calls other relevantPhotosContract.View
methods such as for showing/hiding progress bar and for showing Toast messages. The Presenter subscribes to itsPhotosContract.View
lifecycle by allowing itsPhotosContract.View
to call its ownonViewActive()
andonViewInactive()
to reference/unreference itsPhotosContract.View
. The Presenter also checks if itsPhotosContract.View
is active and notnull
before calling any of itsPhotosContract.View
methods.PhotosContract.Presenter
: theinterface
that exposes functionalities of the presenter,PhotosPresenter
, to be used by the presenter’sView
PhotoDetailPresenter
: thePresenter
responsible forPhotoDetailContract.View
which is implemented byPhotoDetailFragment
. It fetches photo data by callingDataRepository
at the request of itsPhotoDetailContract.View
and delivers that data back to itsPhotoDetailContract.View
. The Presenter also calls other relevantPhotoDetailContract.View
methods such as for showing/hiding progress bar and for showing Toast messages. The Presenter subscribes to itsPhotoDetailContract.View
lifecycle by allowing itsPhotoDetailContract.View
to call its ownonViewActive()
andonViewInactive()
to reference/unreference itsPhotoDetailContract.View
. The Presenter also checks if itsPhotoDetailContract.View
is active and notnull
before calling any of itsPhotoDetailContract.View
methods.PhotoDetailContract.Presenter
: theinterface
that exposes functionalities of the presenter,PhotoDetailPresenter
, to be used by the presenter’sView
.
Folder Structure
Choosing a relevant folder structure for an app is important as it is the first step to architecting an app properly. A relevant folder structure acts as a guide and reference for a particular architecture as you develop the app.
In the context of the sample app which is using the MVP architectural pattern, the following is a relevant folder structure:-
data/
: contains the components that make up theModel
layer. This includes anyclass
andinterface
that is responsible for fetching, storing and exposing data.ui/
: contains theViews
andPresenters
. They are usually grouped by feature/screen.ui/photos/
: containsFragment
,Presenter
,Adaper
andinterface
responsible for displaying a list of photos and handling user input on the photos screen.ui/photodetail/
: containsFragment
,Presenter
andAdapter
responsible for displaying a photo and its comments on the photo detail screen.util
: contains generic base and helper components to be used across the project.
Conclusion
This article is based on my interpretation of the MVP pattern. There are articles that state that MVP pattern is solely for the presentation layer based on the Clean Architecture pattern. I have read about the Clean Architecture pattern and though I believe that it is a more powerful concept, I also think that it can be complex to start off with. I think being able to refactor or create an app based on the MVP pattern is taking a step in the right direction and a good stepping stone towards Clean Architecture pattern.
I hope you enjoyed the article and found it useful. I do want to level-up as an Android Engineer so I am constantly researching and learning about different constructs everyday.
If you have any feedback or suggestion, please do comment below.
Any criticism is highly appreciated :)
References
- https://github.com/googlesamples/android-architecture/tree/todo-mvp/
- https://labs.ribot.co.uk/android-application-architecture-8b6e34acda65
- https://github.com/android10/Android-CleanArchitecture
- http://engineering.remind.com/android-code-that-scales/