logo

Recently Model-View-Presenter (MVP) design pattern is quite trending in Android, and so we too have started using in our projects at CodeToArt. Lets understand what is MVP. We’ll learn by example of upcoming movies where we’ll get upcoming movies from The Movie Database (TMDb) API and display them. You can get the code from github repo.

screen-shot-2016-09-26-at-6-39-00-pm

 

1. View layer has responsibility to only display views to user. It does not have callbacks/data loaders/business logic making it passive layer of MVP. Activity/Fragments/Adapters of your android application is nothing but ofcourse View layer :).

2. Model layer is data access layer. It access the data from local db or remote REST API/web-service or shared preferences.

3. Presenter layer responsible for presenting data from Model to View layer. It handles background task, invokes operations on model and setting data in view.

MVP implementation in Android

Sequence of Event for use-case of upcoming movies sample

  1. User opens the upcoming movies app
  2. View component (MainActivity) attaches presenter
  3. View component (MainActivity) invokes getConfigurationAndLoadMovies() on Presenter
  4. Presenter gets configuration from Model (TMDbApi) and saves it in SharedPreferences
  5. Presenter then loads upcoming movies from Model (TMDbApi) and passes back to View (MainActivity) through MvpView interface

View layer’s MainActivity below

public class MainActivity extends BaseActivity implements MainMvpView,
        MovieAdapter.MovieAdapterCallback {

    @Inject MainPresenter mMainPresenter;
    @Inject MovieAdapter mMovieAdapter;

    @BindView(R.id.recycler_view_movies) RecyclerView mRecyclerView;
    @BindView(R.id.text_no_movies) TextView mTextNoMovies;
    @BindView(R.id.progress) ProgressBar mProgress;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        activityComponent().inject(this);
        mMainPresenter.attachView(this);
        mMainPresenter.getConfigurationAndLoadMovies();
        mMovieAdapter.setCallback(this);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mRecyclerView.setAdapter(mMovieAdapter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mMainPresenter.detachView();
        CImageLoader.stopImageLoader();
    }

    @Override
    public void showMovies(List<Movie> movies) {
        mTextNoMovies.setVisibility(View.GONE);
        mRecyclerView.setVisibility(View.VISIBLE);
        mMovieAdapter.setMovies(movies);
        mMovieAdapter.notifyDataSetChanged();
    }

    @Override
    public void showMovieProgress(boolean show) {
        if (show && mMovieAdapter.getItemCount() == 0) {
            mProgress.setVisibility(View.VISIBLE);
        } else {
            mProgress.setVisibility(View.GONE);
        }
    }

    @Override
    public void showEmptyMessage() {
        mTextNoMovies.setVisibility(View.VISIBLE);
        mRecyclerView.setVisibility(View.GONE);
    }

    @Override
    public void showMovieLoadError(Throwable throwable) {
        mTextNoMovies.setText(throwable.getLocalizedMessage());
        mTextNoMovies.setVisibility(View.VISIBLE);
        mRecyclerView.setVisibility(View.GONE);
    }

    @Override
    public void onMovieClicked(Movie movie) {
        Intent intent = new Intent(this, MovieDetailsActivity.class);
        intent.putExtra(Movie.INTENT_MOVIE, movie);
        startActivity(intent);
    }
}

If you closely observe above code, it only have methods which is related to displaying views. It is completely linear and quite simple activity class :). As you can see, View holds reference to Presenter. It responds to user interaction/events by calling methods of Presenter.

Please note "implements MvpView" here. We’ll describe this more in Presenter layer

Model’s package structure shown below

screen-shot-2016-10-03-at-5-23-43-pm

It is divided into packages namely, local (shared preferences & SQLite adapter goes here), remote (REST API calls) & model where we will parse the data into. They are all accessible from DataManager class. In case of large application, we can have DataManager for each module. Say if we are developing large application for Movies using TMDb API, we can have MovieDataManager, ActorsDataManager, UserDataManager and so on..

Presenter layer, as name suggest, it presents data from Model layer to View layer through interface MvpView. Note MainMvpView on arrow between MainPresenter & MainActivity. Each Presenter is only attached to only one View.

public class MainPresenter {
    private final DataManager mDataManager;
    private final PreferencesHelper mPreferenceHelper;
    private Subscription mSubscription;
    private MainMvpView mMvpView;

    @Inject
    public MainPresenter(DataManager dataManager, PreferencesHelper preferencesHelper) {
        this.mDataManager = dataManager;
        this.mPreferenceHelper = preferencesHelper;
    }

    @Override
    public void attachView(MainMvpView mvpView){
        this.mMvpView = mvpView;
    }
    @Override
    public void detachView() {
        mSubscription.unsubscribe();
    }

    public void getConfigurationAndLoadMovies() {
        mSubscription = Observable.zip(mDataManager.getConfiguration(), mDataManager.getMovies(),
                new Func2<TMDbApi.Response.Metadata, TMDbApi.Response.MovieResponse,
                        List<Movie>>() {
                    @Override
                    public List<Movie> call(TMDbApi.Response.Metadata metadata,
                                            TMDbApi.Response.MovieResponse movieResponse) {
                        metadata.save(mPreferenceHelper);
                        return movieResponse.getResults();
                    }
                }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<List<Movie>>() {
                    @Override
                    public void onCompleted() {
                    }

                    @Override
                    public void onError(Throwable e) {
                        mMvpView.showMovieProgress(false);
                        mMvpView.showMovieLoadError(e);
                    }

                    @Override
                    public void onNext(List<Movie> movies) {
                        mMvpView.showMovieProgress(false);
                        if (movies.isEmpty()) {
                            mMvpView.showEmptyMessage();
                        } else {
                            mMvpView.showMovies(movies);
                        }
                    }
                });
    }
}

MainMvpView

public interface MainMvpView extends MvpView {

    void showMovies(List<Movie> movies);

    void showMovieProgress(boolean show);

    void showEmptyMessage();

    void showMovieLoadError(Throwable throwable);
}

So, Presenter do all task of getting data from server using DataManager and presenting to View through interface MvpView. View don’t access to DataManager directly. So, Presenter, holds reference to View (MvpView interface) and calls exposed methods of View.

How MVP is different from other design patterns?

With Model-View pattern, we end up with connecting all view with all models. This makes project fragile & is really not easy to maintain.

screen-shot-2016-10-03-at-8-38-09-pm

Model-View-Controller (MVC) also similar to MVP where Controller mediate between view and model like Presenter. But unlike,  MVP where View is passive, MVC have smart View layer and updates/retrieves from model.

screen-shot-2016-10-03-at-9-00-48-pm

Conclusion

With MVP,

  • View layer which is interface to user, is super simple. So, it increases the reliability of application.
  • Complex task splits into simpler task making it easier to debug
  • Better separation between layers increases Application testability.
AUTHOR: Mahavir Jain

Founder @CodeToArt, Leads Android Development at CodeToArt.

2 Comments
  • Vikas

    nice article. very well explained.

    February 6, 2017
  • vikram

    can u please convert the same example into MVP without using dagger

    April 15, 2017

Leave a Comment

Your email address will not be published.