Android Architecture

Android MVP Architecture for Beginners (Demo App)

Android MVP Architecture is address the common difficulties such as maintainability and testability. So in this article, We will discuss the various difficulties that address by Android MVP Architecture

What is MVP ..?

MVP (Model View Presenter) design pattern is derived from MVC pattern most of the things is same, where is the controller is replaced by presenter. The MVP design pattern is set of guidelines that should follow for better code reusability and testability. As per MVP guidelines application divided into three part Model, View Presenter.

Key point of MVP design pattern

  • MVP pattern addresses the common difficulties in android app, especially problem is related to maintainability and testability.
  • Model View Presenter increases the view separation of the concern and facilitated unit testing .
  • 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 transparent.
  • This way the application become more simpler, overall application reliability increases remarkable. The application code become cleaner and readable. Code maintainability become 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 component. In this case, it reduces the interaction with Activity and Fragments bare minimum by using Presenter. The Pressenter simply a controller like class to handle presentation logic and update view accordingly.  Using presenter we can write business logic in 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 ?

MVP in android

Just review the above figure that give explanation How MVP is work. Suppose they have shown some user information with 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 architecture each layer have own models and models contains data relevant only  to that layer.

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 from remote 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 Model , It is also contain business logic to the presentation of data .

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 model directly, The view have reference of the presneter and propagate the event UI to Presenter. For example onClick event lifecycle event etc, ,

The Views is expose methods which control presentattion of  data or model for instance show or hide certain element.

Few important things keep in mind while using any design pattern .

As per clean architecture – One layer must be depend on the layer below

  • View depend on presenter and presenter must be depeded 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
  • 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 as 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’m create login contract interface. Just create an 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 seeing we are using a LoginRepository. So create a new interface which provide user instance and save the user instance in 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 names 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 package. So all project structure is looks like below figure.

 Android MVP Architecture for Beginners
MVP Project Structure

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 dagger without explanation. Just put as per giving 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 

Download Sample Project- Android MVP Architecture

Leave a Reply

2000