Filed Under: Android

In this tutorial, we’ll be discussing the android MVP principles and develop an application based on it. Since the start, we’ve been developing applications by adding all the business logic inside the Activity. Let’s analyze the cons of our current approach before digging into the new principles.

Android MVP

With our current approach, the MainActivity class contains all the implementation logic of our application. We’ve been using stuff ranging from Retrofit callbacks to data models(SharedPref, POJO classes) all inside the Activity class.

Eventually, our Activities become god classes and cause problems in maintainability, readability, scalability and refactoring an already bloated code.

Unit testing gets tough since the implementation logic is tightly coupled with the Android APIs. This is where MVP ( Model View Presenter) comes in handy. It allows us to write a clean and flexible code base while giving the luxury to switch any part of the code without much hassle.

Model View Presenter

Model View Presenter divides our application into three layers namely the Model, View and Presenter.

  1. Model: This handles the data part of our application
  2. Presenter: It acts as a bridge that connects a Model and a View.
  3. View: This is responsible for laying out views with the relevant data as instructed by the Presenter

Note: The View never communicates with Model directly.

Android MVP Architecture

The diagram below depicts a basic MVP structure.
android mvp

Android MVP Guidelines

  1. Activity, Fragment and a CustomView act as the View part of the application.
  2. The Presenter is responsible for listening to user interactions (on the View) and model updates (database, APIs) as well as updating the Model and the View.
  3. Generally, a View and Presenter are in a one to one relationship. One Presenter class manages one View at a time.
  4. Interfaces need to be defined and implemented to communicate between View-Presenter and Presenter-Model.
  5. The Presenter is responsible for handling all the background tasks. Android SDK classes must be avoided in the presenter classes.
  6. The View and Model classes can’t have a reference of one another.

Having covered the theory of MVP architecture, let’s build an android MVP app now.

Android MVP Example App Project Structure

android mvp app project structure

The android mvp project consists of 3 interface files (also known as contracts). The Impl files are where the interfaces are implemented.

We’ll be creating a single activity application that’ll display a random quote from a list of quotes present in an ArrayList. We’ll see how the presenter manages to keep the business logic of the application away from the activity class.

Note: Interactors are classes built for fetching data from your database, web services, or any other data source.

Android MVP app code

The code for the activity_main.xml layout is given below.


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.journaldev.hellomvp.MainActivity">

    <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Implementing MVP Pattern in this Application."
        app:layout_constraintBottom_toBottomOf="parent"
        android:padding="8dp"
        android:gravity="center"
        android:textAppearance="?android:attr/textAppearanceSearchResultTitle"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:id="@+id/textView" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="GET NEXT QUOTE"
        android:layout_margin="@android:dimen/notification_large_icon_height"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleLarge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="parent"/>

</android.support.constraint.ConstraintLayout>

The code for the GetQuoteInteractor interface is given below.


package com.journaldev.hellomvp;

public interface GetQuoteInteractor {

    interface OnFinishedListener {
        void onFinished(String string);
    }

    void getNextQuote(OnFinishedListener listener);
}

It contains a nested interface onFinishedListener. We’ll be using a handler inside our GetQuoteInteractorImpl.java below. On completion of the handler, the above onFinished method would be triggered.


package com.journaldev.hellomvp;

import android.os.Handler;

import java.util.Arrays;
import java.util.List;
import java.util.Random;

public class GetQuoteInteractorImpl implements GetQuoteInteractor {

    private List<String> arrayList = Arrays.asList(
            "Be yourself. everyone else is already taken.",
            "A room without books is like a body without a soul.",
            "You only live once, but if you do it right, once is enough.",
            "Be the change that you wish to see in the world.",
            "If you tell the truth, you don't have to remember anything."
    );

    @Override
    public void getNextQuote(final OnFinishedListener listener) {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                listener.onFinished(getRandomString());
            }
        }, 1200);
    }

    private String getRandomString() {

        Random random = new Random();
        int index = random.nextInt(arrayList.size());

        return arrayList.get(index);
    }
}

The MainView.java interface is defined below.


package com.journaldev.hellomvp;

public interface MainView {

    void showProgress();

    void hideProgress();

    void setQuote(String string);
}

showProgress() and hideProgress() would be used for displaying and hiding the progressBar while the next random quote is fetched from the GetQuoteInteractorImpl class.

setQuote() will set the random string on the textView.

The code for the MainPresenter.java interface is given below.


package com.journaldev.hellomvp;

public interface MainPresenter {

    void onButtonClick();

    void onDestroy();
}

The MainPresenterImpl.java class is defined below.


package com.journaldev.hellomvp;

public class MainPresenterImpl implements MainPresenter, GetQuoteInteractor.OnFinishedListener {

    private MainView mainView;
    private GetQuoteInteractor getQuoteInteractor;

    public MainPresenterImpl(MainView mainView, GetQuoteInteractor getQuoteInteractor) {
        this.mainView = mainView;
        this.getQuoteInteractor = getQuoteInteractor;
    }

    @Override
    public void onButtonClick() {
        if (mainView != null) {
            mainView.showProgress();
        }

        getQuoteInteractor.getNextQuote(this);
    }

    @Override
    public void onDestroy() {
        mainView = null;
    }

    @Override
    public void onFinished(String string) {
        if (mainView != null) {
            mainView.setQuote(string);
            mainView.hideProgress();
        }
    }
}

The above class implements the Presenter and nested interface from GetQuoteInteractor. Moreover it instantiates the MainView and GetQuoteInteractor interfaces (View and Model respectively).

onButtonClick method would be triggered in the MainActivity class when the button is clicked and will display a progressBar while it gets the next random quote.

onDestroy() method would be invoked inside the lifecycle method onDestroy() of the MainActivity.

onFinished() method gets called when the handler is completed inside the GetQuoteInteractorImpl. It returns the string which will be displayed in the TextView using the MainView’s instance.

The code for the MainActivity.java which implements MainView interface is given below.


package com.journaldev.hellomvp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

import static android.view.View.GONE;

public class MainActivity extends AppCompatActivity implements MainView {

    private TextView textView;
    private Button button;
    private ProgressBar progressBar;
    MainPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = (TextView) findViewById(R.id.textView);
        button = (Button) findViewById(R.id.button);
        progressBar = (ProgressBar) findViewById(R.id.progressBar);
        presenter = new MainPresenterImpl(this, new GetQuoteInteractorImpl());

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.onButtonClick();
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter.onDestroy();
    }

    @Override
    public void showProgress() {
        progressBar.setVisibility(View.VISIBLE);
        textView.setVisibility(View.INVISIBLE);
    }

    @Override
    public void hideProgress() {
        progressBar.setVisibility(GONE);
        textView.setVisibility(View.VISIBLE);
    }

    @Override
    public void setQuote(String string) {
        textView.setText(string);
    }
}

showProgress() and hideProgress() methods shows and hides the textView as well to prevent overlapping the current text and the progressBar.

Note: The above class contains is now devoid of the business logic and is responsible for only updating the UI based on the changes triggered by the Presenter layer.

The output of the above android MVP application is given below.
android mvp pattern app example

Note: Google recommends to keep a single contract interface file for the Model View and Presenter.

So let’s club the interfaces defined above into one.

The project structure now looks like this:
android MVP hello world app

The code for the MainContract.java interface is given below.


package com.journaldev.hellomvp;

public interface MainContract {

    interface MainView {
        void showProgress();

        void hideProgress();

        void setQuote(String string);
    }

    interface GetQuoteInteractor {
        interface OnFinishedListener {
            void onFinished(String string);
        }

        void getNextQuote(OnFinishedListener onFinishedListener);
    }

    interface Presenter {
        void onButtonClick();

        void onDestroy();
    }
}

This brings an end to this tutorial. There’s a lot more to explore in MVP pattern that we’ll be discussing soon.

You can download the Android MVP Hello World Project from the link below.

Reference: Wikipedia

Comments

  1. Jishnu says:

    Hey guys,

    I have made an App with complete MVP which is very simple .Please check the link.

    https://github.com/kiterunner20/TruckeeApp-

  2. Afreen says:

    This has been extremely helpful. Do you have more samples of MVP using third party libraries?

  3. Amin says:

    thank you for this clean and ‘not a single line longer than what it needs to be’ tutorial!
    I also have a question. If multiple views (activities/fragments) are using the same model (or different parts of it), which one of those views’ interface should have the interface of for interacting with that model? isn’t it better practice to keep the model interface separate from those of view and presenter (since they are one-on-one)?

    And also isn’t better for the GetQuoteInteractor interface to be ‘extended’ by MainPresneter interface than to be ‘implemented’ by the MainPresneterImpl? (this way, the MainPresenterImpl just has to implement an interface which declares everything it should do. I think it’s kind of cleaner! but you’re the boss 😉 )

  4. Vishal Bhandare says:

    In Guideline section, The Presenter is responsible for handling all the background tasks. I think it should be Model and not Presenter

  5. Sandeep Singh says:

    The above class implments

    Implements spelling is wrong. Please fix it.

    1. Pankaj says:

      Thanks for noticing the typo, fixed it.

  6. Monika Sharma says:

    Am gettting error when i used maincontractor interface
    Caused by: java.lang.ClassCastException: com.example.monikasharma.demomvpproject.MainActivity cannot be cast to com.example.monikasharma.demomvpproject.view.MainView
    at com.example.monikasharma.demomvpproject.presenter.impl.MainPresenterImpl.(MainPresenterImpl.java:22)

    1. Shail says:

      Check if you have implemented MainView in MainActivity
      you class should look like
      public class MainActivity extends AppCompatActivity implements MainView

  7. Chetna Wankar Wadikar says:

    please tell me bifurcation of class according to MVP pattern in this as i understand GetQuoteInteractor,GetQuoteInteractorImpl is model , MainView,MainActivity is View and MainPresenter, MainPresenterImpl is Presenter .

    Please correct me if im wrong

    1. Anupam says:

      Hi Chetna,
      Yes, the GetQuoteInteractorImpl and MainPresenterImpl classes are the Model and Presenters respectively.
      GetQuoteInteractor, MainPresenter are interfaces.

  8. Ranjith Srinivas says:

    As mentioned in the point number 6th:

    The View and Model classes can’t have a reference of one another.
    But u have created the Object of the model class ie GetQuoteInteractorImpl in the view ie in MainActivity java file .
    I think u need to remove the code and create the same in the PresenterClass constructor

    pls find the snippet code for the same :

    MainActivtity {

    presenter = new MainPresenterImpl(this, new GetQuoteInteractorImpl());

    }

    1. Anupam says:

      Nice observation Ranjith!
      The code in the MainActivity just calls a reference to the Presenter.
      Inside the Presenter, we make a reference to the Model class.
      So it isn’t exactly inside the MainActivity.
      Besides we can always use Dagger2 to avoid dependency calls everywhere using the new keyword.
      Here’s a tutorial for the same.

  9. Raj says:

    Why you missed out Model ?

    1. Omar Dhanish says:

      where is model in this tutorial ? I think Interactor is mmodel right ??

  10. utpaul says:

    Tnx its a great tutorial ……………….

  11. Kanwal says:

    why we have used nested interface

    interface GetQuoteInteractor {
    interface OnFinishedListener {
    void onFinished(String string);
    }
    }

    for what purpose? what are the other options besides this?

    1. Suleman says:

      Its Google Recommendation For Good Practice as it makes Managing them Easy

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages