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.

Model-View-Presenter

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

Model’s package structure

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.

design patterns

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.

Model-View-Controller

Conclusion

With MVP,

Do you have any product idea or business need?

How TO MAKE YOUR APP WORK OFFLINE

HOW TO MAKE YOUR APP WORK OFFLINE

Offline mobile app development is critical for users to sync their data properly, when offline. Here, we help you learn the process from implementation to data synchroniz

Omnivore POS integration - Ensuring Agility To Your Restaurant Businesses

Omnivore POS integration - Ensuring Agility To Your Restaurant Businesses

Omnivore software offers a point-of-sales integration API making the restaurant system agile, more customer engaging and adaptive to fast changing business environments.

Unit Testing using Mockk.io

Unit Testing using mockK.io in Kotlin

Learn about unit testing using mockk.io in Kotlin.