Android MVP Architecture addresses common difficulties such as maintainability and testability. So in this article, We will discuss the various difficulties that addressed by Android MVP Architecture
What is MVP ..?
MVP (Model View Presenter) design pattern is derived from
Key point of MVP design pattern
- MVP pattern
addresses the common difficulties inandroid app, especially problem is related to maintainability and testability. - Model View Presenter increases the
view separation of the concern and facilitated unittesting . - In the MVP pattern, we can separate background task from activity, fragment views to make them independent of most life cycle related event
- Using a consistent architectural and design pattern the development process become much more consistent, a lot of easier and more transparent
. This way the application becomes simpler, and overall application reliability increases remarkable. The application code becomes cleaner and more readable. Code maintainabilitybecome better and most importantly becomes fun and developer are Happy🙂 .
How MVP work ?
The MVP pattern is user interface software architecture pattern that reduces the behavior of UI components. In this case, it reduces the interaction with Activity and Fragments bare minimum by using Presenter. The Pressenter is simply a controller-like class to handle presentation logic and update view accordingly. Using presenter we can write business logic in the presenter layer that have minimum interaction with views that make much easier to test and much faster to test. Why it makes faster to test, well simply put we no longer need to test input and output. the android content is gone so we no need of android emulator and device, We just need on JVM for running test which is quite fast.
Flow of MVP work ?

Just review the above figure that gives an explanation How MVP is work. Suppose they have shown some user information with a couple of variable like first and last name. The user makes change in first and last name and save the information on by clicking on save button. In this state the presenter will be involved by obtaining first and last name, now presenter will check these are valid or not. If value invalid then presenter makes the error and show the error message on the view. However, the case value is correct the update the model and send callback to view showing updated value on the views.
The Robert C Martin say in A Handbook of Agile Software Craftsmanship – The one way to make deadline – the one way to go fast – Is the keep the code as clean as possible all times.
Layer of MVP architecture – Model,View and Presenter
Model
In the clean
For example
- View have own ViewModel to present data in the view
- Database Model may
be need to retrieve database entity - Simliery Network model may be
to needed represent data entries to retrieve fromremote server - When data move between layer – the Model is transformed from one layer representation layer to another.
Presenter
A Presenter is a middle man between views and
The Presenter the retrive the data from model and format the data before passing the view . The Presenter also update the UI via View.
View
In MVP design pattern The Activity and Fragment and View is passive and can not access
The Views is exposed methods that control the presentation of data or model for instance show or hide certain elements.
A few important things keep in mind while using any design pattern.

As per clean architecture – One layer must depend on the layer below
- View depends on presenter and presenter must be depended on use case
- Code can not jump over the such as view can not access the use case and presenter can not access the enity
- The remarkable thing Dependency is followed downward see the flow diagram
MVP Sample App
Now we will a sample app following a MVP design pattern. Open Android Studio and create a new project. Enter project name like MVP example and select empty template and change activity name to LoginActivity .
Open activity_login.xml and add below code
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center" android:padding="@dimen/_8dp"> <EditText android:id="@+id/loginActivity_firstName_editText" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="First Name" tools:text="First Name" /> <EditText android:id="@+id/loginActivity_lastName_editText" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Last Name" android:layout_marginTop="@dimen/_8dp" tools:text="Last Name" /> <Button android:id="@+id/loginActivity_login_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorAccent" android:textColor="@color/colorWhite" android:layout_marginTop="@dimen/_16dp" android:text="Log in" /> </LinearLayout>
Add dimens value in dimens.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <dimen name="_16dp">16dp</dimen> <dimen name="_8dp">8dp</dimen> <dimen name="default_padding">4dp</dimen> </resources>
Create a model class with named User.Java , which hold some user properties with getter setter such as id, first name, last name
package com.wave.mvpexample.data.model; public class User { private int id; private String firstName; private String lastName; public User(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public void setId(int id) { this.id = id; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } }
For using MVP design pattern, Now I create login contract interface. Just create a new interface inside src folder and add below code
package com.wave.mvpexample.login; import com.wave.mvpexample.data.model.User; public interface LoginActivityMVP { interface View{ String getFirstName(); String getLastName(); void showInputError(); void setFirstName(String firstName); void setLastName(String lastName); void showUserSavedMessage(); } interface Presenter { void setView(View view); void loginButtonClicked(); void getCurrentUser(); } interface Model { void createUser(String name, String lastName); User getUser(); } }
Create a model named is LoginModel
Just create a new class java which implements LoginActivityMVP.Model and add below code
package com.wave.mvpexample.data.model; import com.wave.mvpexample.data.repo.LoginRepository; import com.wave.mvpexample.login.LoginActivityMVP; public class LoginModel implements LoginActivityMVP.Model { private LoginRepository repository; public LoginModel(LoginRepository repository) { this.repository = repository; } @Override public void createUser(String name, String lastName) { repository.saveUser(new User(name, lastName)); } @Override public User getUser() { return repository.getUser(); } }
Create a Repository for playing model
In LoginModel you see we are using a LoginRepository. To create a new interface which provides user instance and save the user instance in the repository.
package com.wave.mvpexample.data.repo; import com.wave.mvpexample.data.model.User; public interface LoginRepository { User getUser(); void saveUser(User user); }
Let’s create a repository named is UserRepository
Now create a java file which implements LoginRepository and add block of code
package com.wave.mvpexample.data.repo; import com.wave.mvpexample.data.model.User; public class UserRepository implements LoginRepository { private User user; @Override public User getUser() { if (user == null) { User user = new User("Dinesh", "Kumar"); user.setId(0); return user; } else { return user; } } @Override public void saveUser(User user) { if (user == null) { user = getUser(); } this.user = user; } }
Create Presenter for LoginActivity
As per MVP, we have created Model, View on above, Now we will create a presenter for LoginActivity, Just create a java class with named is LoginActivityPresenter which implementing LoginActivityMVP.Presenter. In presenter we are doing the following operation.
- Pass the view instance in the presenter in setView(View mView) methods
- Defining onClick() event in loginButtonClicked() methods
- Body on getCurrentUser() methods is retruning user instance
package com.wave.mvpexample.login; import android.support.annotation.Nullable; import com.wave.mvpexample.data.model.User; public class LoginActivityPresenter implements LoginActivityMVP.Presenter { @Nullable private LoginActivityMVP.View view; private LoginActivityMVP.Model model; public LoginActivityPresenter(LoginActivityMVP.Model model) { this.model = model; } @Override public void setView(LoginActivityMVP.View view) { this.view = view; } @Override public void loginButtonClicked() { if (view != null) { if (view.getFirstName().trim().equals("") || view.getLastName().trim().equals("")) { view.showInputError(); } else { model.createUser(view.getFirstName(), view.getLastName()); view.showUserSavedMessage(); } } } @Override public void getCurrentUser() { User user = model.getUser(); if (user != null) { if (view != null) { view.setFirstName(user.getFirstName()); view.setLastName(user.getLastName()); } } } }
I have distributed project in different packages. So all project structure looks like below figure.

Open LoginActivity and implements LoginActivityMVP.View
package com.wave.mvpexample.login; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import com.wave.mvpexample.R; import com.wave.mvpexample.root.App; import javax.inject.Inject; public class LoginActivity extends AppCompatActivity implements LoginActivityMVP.View { @Inject LoginActivityMVP.Presenter presenter; private EditText firstName; private EditText lastName; private Button login; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); ((App) getApplication()).getComponent().inject(this); firstName = (EditText) findViewById(R.id.loginActivity_firstName_editText); lastName = (EditText) findViewById(R.id.loginActivity_lastName_editText); login = (Button) findViewById(R.id.loginActivity_login_button); login.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { presenter.loginButtonClicked(); } }); } @Override protected void onResume() { super.onResume(); presenter.setView(this); presenter.getCurrentUser(); } @Override public String getFirstName() { return firstName.getText().toString(); } @Override public String getLastName() { return lastName.getText().toString(); } @Override public void showInputError() { Toast.makeText(this, "First Name or last name cannot be empty", Toast.LENGTH_SHORT).show(); } @Override public void setFirstName(String firstName) { this.firstName.setText(firstName); } @Override public void setLastName(String lastName) { this.lastName.setText(lastName); } @Override public void showUserSavedMessage() { Toast.makeText(this, "User saved successfully", Toast.LENGTH_SHORT).show(); } }
In this project, we are using Dagger2 with MVP. So don’t think much about Dagger2 read our another article with a complete explanation of Dagger2. In this project, I’m using a dagger without explanation. Just put as per given instruction
Create a LoginModule
Now create a java class named is LoginModule.java which annotated with @Module annotation
package com.wave.mvpexample.login; import com.wave.mvpexample.data.model.LoginModel; import com.wave.mvpexample.data.repo.LoginRepository; import com.wave.mvpexample.data.repo.UserRepository; import dagger.Module; import dagger.Provides; @Module public class LoginModule { @Provides public LoginActivityMVP.Presenter provideLoginActivityPresenter(LoginActivityMVP.Model model){ return new LoginActivityPresenter(model); } @Provides public LoginActivityMVP.Model provideLoginActivityModel(LoginRepository repository){ return new LoginModel(repository); } @Provides public LoginRepository provideLoginRepository(){ return new UserRepository(); } }
Furthermore create a AppModule
create a new package inside base package name is root. Now create a new class name AppModule in root package.
package com.wave.mvpexample.root; import android.app.Application; import android.content.Context; import javax.inject.Singleton; import dagger.Module; import dagger.Provides; @Module public class AppModule { private Application application; public AppModule(Application application) { this.application = application; } @Provides @Singleton public Context provideContext() { return application; } }
Now create AppComponent
Let’s create a interface name is AppComponent and add below code
package com.wave.mvpexample.root; import com.wave.mvpexample.login.LoginActivity; import com.wave.mvpexample.login.LoginModule; import javax.inject.Singleton; import dagger.Component; @Singleton @Component(modules = {AppModule.class, LoginModule.class}) public interface AppComponent { void inject(LoginActivity target); }
After that create a Application
Now create class with extends Application add this code
package com.wave.mvpexample.root; import android.app.Application; import com.wave.mvpexample.login.LoginModule; public class App extends Application { private AppComponent component; @Override public void onCreate() { super.onCreate(); component = DaggerAppComponent.builder() .appModule(new AppModule(this)) .loginModule(new LoginModule()) .build(); } public AppComponent getComponent() { return component; } }
Add App in AndroidManifest
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.wave.mvpexample"> <application android:name=".root.App" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" tools:ignore="GoogleAppIndexingWarning"> <activity android:name=".login.LoginActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Finally, just rebuild the project, after rebuilding the DaggerAppComponent will be created. Then import the class and Run the project. This is the best way to using an Android MVP Architecture design pattern, activity will always clean and the code is maintainable and testable.
We have created a sample app with MVP Architect Android apps with Dagger2, Retrofit & RxJava, RoomPersistence. Must Read