Android App From Scratch Part 1 — Model-View-Presenter

In this tutorial series, I will try to create an RSS Reader app step by step. I aim to explain you about how to write scalable, testable and easy to read/maintain code. And MVP approach that perfectly fits to an Android application.

Through this series I will explain:

  1. How to use Model-View-Presenter in an Android App
  2. Implementing must have libraries
  3. Implementing App Logic
  4. Creating unit tests with JUnit
  5. Creating Android Instrumentations tests
  6. Continuous Integration with Travis-CI
  7. Making the app ready for Play Store

I assume that you are familiar with Android Development. So I will not explain each detail. Feel free to ask anything by commenting to my post.

Many people say that MVP is not an architecture. It lets you have layered code by organising. It will be easy to READ, TEST and SCALE the code.

If you need to to same jobs in different places you should have your base classes. BaseActivity, BaseFragment, BasePresenter etc.

Here we go with base classes.

BaseMvpPresenter.java

/**
* Each presenter must implement this interface
*
*
@param <V> View for the presenter
*/
public interface BaseMvpPresenter<V extends BaseView> {

/**
* Called when view attached to presenter
*
*
@param view
*/
void attach(V view);

/**
* Called when view is detached from presenter
*/
void detach();

/**
*
@return true if view is attached to presenter
*/
boolean isAttached();
}

Each presenter is an interface and will implement BaseMvpPresenter Creating the logic over interfaces instead of classes will be useful.

BasePresenter.java

public class BasePresenter<V extends BaseView> implements BaseMvpPresenter<V> {

/**
* Attached view
*/
private V mView;


@Override
public void attach(V view) {
mView = view;
}

@Override
public void detach() {
mView = null;
}

@Override
public boolean isAttached() {
return mView != null;
}

public V getView() {
return mView;
}
}

Each presenter should have attach()/detach() or (bind/unbind) methods to bind the view to the presenter. If an asynchronous job is done we have to check if view is attached or not. So we also need a isAttached()method.

Also view is defined as generic class extends BaseView. By doing this we tell the presenter that it should attach a view that extends the BaseView.

BaseView.java

public interface BaseView {
}

This is an interface that all views should implement. I leave it blank for now. But I will ad some methods here.

BaseActivity.java

public abstract class BaseActivity extends AppCompatActivity implements BaseView {


@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getContentResource());
init(savedInstanceState);
}


/**
* Layout resource to be inflated
*
*
@return layout resource
*/
@LayoutRes
protected abstract int getContentResource();


/**
* Initialisations
*/
protected abstract void init(@Nullable Bundle state);

}

MVP says that your views (Activity, Fragment or ViewGroup) should be as dumb as possible. Because you will keep your logic in presenter classes.

I also created an init() method that triggers in onCreate() for some initialisations.

Note: Don’t use this code in production. We will use dependency injection to create presenter instance. With DI presenter creation will be in BaseActivity Don’t forget this is the first step 😃

Each activity should have layout resource file. I moved it to an abstract class. It is your choice you may not use it.

Now we have our base classes that we need. It is time to add our extra small feature Activity.

Each activity should have a presenter class and a group of interfaces in a contract interface.

MainContract.java

public interface MainContract {

// User actions. Presenter will implement
interface Presenter extends BaseMvpPresenter<MainContract.View>{
void loadHelloText();
}

// Action callbacks. Activity/Fragment will implement
interface View extends BaseView {
void onTextLoaded(String text);
}

}

This is the signature of our feature. We group methods under two domains.

  1. Presenter interface includes all user actions. (click, swipe, delete etc.)
  2. View interface includes all callbacks and UI changes (show loading, populate a list, show connection error etc.)

Here we have only one action loadHelloText() and a response onTextLoaded(String text) for this action.

MainPresenter.java

public class MainPresenter extends BasePresenter<MainContract.View> implements MainContract.Presenter {

private String[] helloTexts = {"BONJOUR", "HOLA", "HALLO", "MERHABA", "HELLO", "CIAO", "KONNICHIWA"};

@Override
public void loadHelloText() {
Random random = new Random();
String hello = helloTexts[random.nextInt(helloTexts.length)];
getView().onTextLoaded(hello);
}
}

I extended BasePresenter and say that MainContract.View is attached to the activity. Then implement MainContract.Presenter to our presenter. So our presenter has all possible user actions.

Layout for the activity:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="me.topas.rssreader.MainActivity">

<TextView
android:id="@+id/tvHello"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="20sp" />
</RelativeLayout>

MainActivity.java

public class MainActivity extends BaseActivity implements MainContract.View, View.OnClickListener {


private TextView mTextView;

private MainPresenter mPresenter;

@Override
protected int getContentResource() {
return R.layout.activity_main;
}

@Override
protected void init(@Nullable Bundle state) {
mTextView = (TextView) findViewById(R.id.tvHello);
mTextView.setOnClickListener(this);
mPresenter = new MainPresenter();
mPresenter.attach(this);
mPresenter.loadHelloText();
}

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

@Override
public void onTextLoaded(String text) {
mTextView.setText(text);
}


@Override
public void onClick(View view) {
mPresenter.loadHelloText();
}
}

I created a presenter instance then attached it. DO NOT do it in production code. Because I will do it with DI in next parts.

After attaching, loadHelloText() method of presenter is called. Presenter decides what to show in hello text, then pass it to the view with onTextLoaded(String text) callback.

Here is the source code of this part:

This is the very basic implementation of MVP. In the next parts I will give more details and real code. You can find different examples of MVP below:

You can go on reading with Part2

If you liked the article, click the 💚 below so more people can see it! Also, you can follow me on Medium