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.
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
- User opens the upcoming movies app
- View component (
MainActivity
) attaches presenter - View component (
MainActivity
) invokesgetConfigurationAndLoadMovies()
on Presenter - Presenter gets configuration from Model (TMDbApi) and saves it in SharedPreferences
- Presenter then loads upcoming movies from Model (TMDbApi) and passes back to View (
MainActivity
) throughMvpView
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
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.
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.
Conclusion
With MVP,
- The view layer which is the interface to a user, is super simple. So, it increases the reliability of the application.
- Complex task splits into simpler task making it easier to debug
- Better separation between layers increases Application testability.