A Simple MVP Approach for Android

What is Model View Presenter?

The two-second summary is: It’s a way to break up your code into chunks that each have a clear purpose.

Model

Holds information your application (or feature) needs and helps to update that information when required (this isn’t strictly correct, but it is simpler if you think of it this way).

View

Shows information to the user and collects information about the user’s actions.

Presenter

Takes information about the user’s actions from the view, manipulates information in the model if required and sends update information to the View to be presented to the user.

(If you do want to get into the finer details, I suggest starting here: Martin Fowler: GUI Architectures. Quite an interesting and useful comparison of MVP with several other patterns.)

 Why are there so many articles on MVP for Android?

It’s hard to get right. The approach I am going to present has been developed out of pure frustration. I tried many, many approaches and frameworks. Some were great until things got complicated and others imposed too many restrictions.

So I laid out a set of criteria for myself and set our to find a better approach.

Guiding criteria for this framework:

  • Simplicity. It should be easy to follow and get right.
  • It should work with the Android life-cycle, not against it.
  • It should not prevent using other frameworks or technologies
  • It should make developing for Android easier and more fun.

BEFORE WE START

Do not copy and paste the code from this article, it isn’t complete or correct. Go get the sample application out of GitHub. Ok?

Now repeat after me:

I WILL NOT TRY TO CUT AND PASTE THE CODE FROM THIS ARTICLE, I WILL GET IT FROM THE GITHUB LINK AT THE END OF THE ARTICLE.

Right, let’s get on with it.

Each component has an interface which declares its public members.

I can hear you groaning already. Yes. You really do have to declare an interface for the Model, View and Presenter. Yes. They do have to be in their own file. Why?

The interfaces define the interactions between the components. This makes the way the components fit together easier to understand, easier to test and in the end, easier to write. One component, one interface. Model, View, Presenter. Three components. Three interfaces.

They need to be in their own file because placing an interface declaration in the same file as the class that implements or realizes that interface introduces tight coupling that renders the interface almost pointless.

But you can ease the pain with nested interfaces. I call these interfaces a “Feature Contract” it defines the shape of the API for each component that makes up a single “Feature” (Complete MVP stack).

Example:

/**
 * Womble Counter Feature Contract
 */
public interface WombleCounterContract {

    /**
     * MVP view interface
     */
    interface View {
        void displayWombleCount(int wombleCount);
    }

    /**
     * MVP Presenter interface
     */
    interface Presenter {
        void onUserCountedAWomble();
    }

    /**
     * MVP Model interface
     */
    interface Model {
        int getCurrentWombleCount();
        void incrementWombleCount();
    }

}

That’s not so bad is it? And you can see all of the component interfaces in a single file.

Building an Android Application with Simple MVP

So. I’ll warn you upfront. I’m going to do quite a bit of glossing over details along the way to hopefully highlight the important bits.
Since we started with a Womble Counter feature contract, lets build a simple Womble counter application. It will have a single activity user interface with a text field to display the number of Wombles we have seen and a button we can press whenever we see one.
Our Activity will be our MVP View..
public class WombleCounterViewActivity
        extends AppCompatActivity
        implements WombleCounterContract.View {

    // A humble Android text view that will show our magnificent Womble count
    private TextView mWombleCountValueView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Fairly standard, set the layout
        setContentView(R.layout.activity_main);

        // And bind our view field to the views in our layout
        mWombleCountValueView = (TextView) findViewById(R.id.activity_main_WombleCounterValue);

        // Set our click button listener
        findViewById(R.id.activity_main_CountWomblerButton).setOnClickListener(
                new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                    }
                }
        );
    }

    @Override
    public void displayWombleCount(int wombleCount) {

        // Display the number of wombles we have seen
        mWombleCountValueView.setText(getString(R.string.wombleCountValueFormat, wombleCount));

        // For best results, try hanging around Wimbeldon Common.
    }
}

Our layout will look like this:Screenshot_1490173843

We then add our Presenter:
public class WombleCounterPresenter
        implements WombleCounterContract.Presenter {

    public WombleCounterPresenter() {
    }

    @Override
    public void onUserCountedAWomble() {
    }
}

and finally our model:

public class WombleCounterModel
        implements WombleCounterContract.Model {

    private int mWombleCount;

    @Override
    public int getCurrentWombleCount() {
        return mWombleCount;
    }

    @Override
    public void incrementWombleCount() {
        // Increment our womble count
        mWombleCount++;
    }
}

But it doesn’t do anything…

None of the components know about each other or how to talk to each other. We need to introduce them to each other.

Now, there are some odd things about the way we are going to connect these components together, it’s a little uncomfortable for old school MVC purists and indeed it made my skin crawl a little at first too.

Lets just say there are some quirks of the Android life-cycle that we have to live with and move on (hint: the view creates the presenter, which feels backwards).

mvp-architecture

Looking at this simple diagram of a general MVP stack you can see that a View holds a strong reference to the Presenter and the Presenter holds a strong reference to the Model. You can also see that the Presenter holds a weak reference to the View and the Model holds a weak reference to the View.

Starting with our View, add a strong reference to a presenter:
public class WombleCounterViewActivity
        extends AppCompatActivity
        implements WombleCounterContract.View {
    ...
    // The brains of the operation; our Presenter
    private WombleCounterContract.Presenter mPresenter;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Fairly standard, set the layout
        setContentView(R.layout.activity_main);

        // And bind our view field to the views in our layout
        mWombleCountValueView = (TextView) findViewById(R.id.activity_main_WombleCounterValue);

        // Set our click button listener
        findViewById(R.id.activity_main_CountWomblerButton).setOnClickListener(
                new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        // When the button is clicked,
                        // tell the presenter that the user counted a Womble..
                        mPresenter.onUserCountedAWomble();
                    }
                }
        );

    }
    ...
}

Notice that we have added a call to mPresenter.onUserCountedAWomble() to our Button click handler. This will inform our Presenter that the user has counted a Womble.Now lets update our Presenter:

public class WombleCounterPresenter
        implements WombleCounterContract.Presenter,
                   WombleCounterContract.ModelListener {
    ...
    private WombleCounterContract.Model mModel;
    private WeakReference mViewWeakReference;
    ...
}

Notice the Presenter also has as a Weak Reference to our View. In case you didn’t know, a WeakReference is a special Java class that can hold an object without preventing it from being garbage collected. You can think of it in this case as a way of preventing memory leaks due to cyclic (or circular) references.Now we get to the model and hit a snag.

Why do we hold a weak connection to the Presenter from the Model? To let the presenter know that the Womble Count has changed (State updates).

But the presenter doesn’t have any members in the contract that seem right.. recall the Presenter interface from our Feature Contract:

interface Presenter {
    void onUserCountedAWomble();
}

That talks about a User counting a womble. It doesn’t talk about the Womble Count in the Model changing. The obvious thing seems to be to add a new member:

interface Presenter {
    void onUserCountedAWomble();
    void onWombleCountChanged();
}

I say seems because there are a couple of problems here:

The presenter interface is used by the View to pass User interactions to the Presenter. How confusing is it to have the Model passing status changes the same way?

Look at the names of those members, how easy would it be to mix them up and call the wrong one from the Model or View?

When you go to write your Unit tests for your model (oh come on, you know you should!) you will undoubtedly need to test the call to onWombleCountChanged() and when you do you will suddenly find you not only have to write mock implementations for onWombleCountChanged() but every other member of the Presenter interface as well. Not very convenient or tidy to say the least.

So what should we do?

My personal preference in this situation is to add an additional interface to our Feature Contract. A Model Listener interface.

/**
 * Womble Counter Feature Contract
 */
public interface WombleCounterContract {
     ...
    /**
     * Model Listener interface
     */
     interface ModelListener {
         void onWombleCountChanged();
     }
}

Now we add a method to set the listener in our Model interface..

/**
 * MVP Model interface
 */
interface Model {
    void setListener(ModelListener listener);
     ...
}

and add it to our Model..

public class WombleCounterModel implements WombleCounterContract.Model {
    ...
    private WeakReference mListenerWeakReference = new WeakReference(null);
    ...
    @Override
    public void setListener(ModelListener listener) {
        mListenerWeakReference = new WeakReference(listener);
    }
    ...
}

and there is our missing Weak Reference.

Now, some of you may be wondering why I didn’t just use the getCurrentWombleCount()  method that already existed in the Model interface?

Well, I could have in this case. But then I probably would have ended up with code in the presenter like this:

public void onUserCountedAWomble() {
    mModel.incrementWombleCount();
    View view = mViewWeakReference.get();
    if (view != null) {
        view.displayWombleCount(mModel.getCurrentWombleCount());
    }
}

At first glance it looks correct, and it will work perfectly well as long as  mModel.incrementWombleCount() and mModel.getCurrentWombleCount() don’t do anything complicated like  saving the result to a database or retrieving the Womble count from a remote server on a slow network.

But it is a potential source of future trouble.

This method makes assumptions about the behavior of the Model. It assumes the Model does not do anything that takes a long time.

It also does two quite different things: it records the User counting a Womble and it updates the User Interface with the new value of the Womble Counter.

What happens if you add a “Clear Counter” button? Do you have to duplicate the code to update the view there as well?

How about your unit tests? Is it easier to test that your Presenter calls mModel.incrementWombleCount or mModel.incrementWombleCount and view.displayWombleCount?

I think it is better for the long term health of your project to implement the Model state update as a Listener. I’m sure in time you will agree even if you don’t right now.

Now we have to make our Presenter “listen” to our Model:

public class WombleCounterPresenter
        implements WombleCounterContract.Presenter,
                   WombleCounterContract.ModelListener {
    ...
    private WombleCounterContract.Model mModel;
    ...
    public WombleCounterPresenter(@NonNull WombleCounterContract.Model model) {
        mModel = model;
        mModel.setListener(this);
    }
    ...
    @Override
    public void onWombleCountChanged() {
        // The model has told us the womble count has changed

        // Get the view out of our weak reference..
        WombleCounterContract.View view = mViewWeakReference.get();
        if (view != null) {
            view.displayWombleCount(mModel.getCurrentWombleCount());
       }
    }
    ...
}

So, we have made our Presenter implement the ModelListener interface.In the constructor of the Presenter I pass in the Model (we’ll talk about that later) and make the Presenter the Model’s listener.

When the Presenter is notified by the Model that the Womble Counter value has changed (by calling onWombleCountChanged() above), our Presenter retrieves its reference to the View and asks it to update the displayed counter value with the new value in the Model.

Our Model doesn’t actually call its listener yet. So why don’t we add that:

public class WombleCounterModel
        implements WombleCounterContract.Model {
    ...
    private int mWombleCount;
    private WeakReference mListenerWeakReference = new WeakReference(null);
    ...
    @Override
    public void incrementWombleCount() {

        // Increment our womble count
        mWombleCount++;

        // Get our listener from the weak reference
        ModelListener listener = mListenerWeakReference.get();

        // If we have a listener,
        if (listener != null) {
            // tell the listener the womble count has changed
            listener.onWombleCountChanged();
        }
    }
    ...
}

So now our Model will call its listener when it has incremented the Womble Counter.If you were to run our Womble Counter at this moment it would crash as soon as you click on the Count Womble button. We haven’t created a presenter or Model yet.
Let’s add that to our onCreate method:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    WombleCounterContract.Model model = new WombleCounterModel();
    mPresenter = new WombleCounterPresenter(model);
    ...
}

Great. But it will still crash. We have a View, a Presenter and a Model, but we haven’t given the Presenter a value for its reference to the View.The obvious thing seems to be to pass it in the constructor. It actually isn’t the right thing to do for reasons we will discover later, but we will do it for now.

public class WombleCounterPresenter
        implements WombleCounterContract.Presenter,
                   WombleCounterContract.ModelListener {
    ...
    private WombleCounterContract.Model mModel;
    private WeakReference mViewWeakReference;
    ...
    public WombleCounterPresenter(@NonNull WombleCounterContract.View view, @NonNull WombleCounterContract.Model model) {
        mViewWeakReference = new WeakReference(view);
        mModel = model;
        model.setListener(this);
    }
    ...
}


Great, so now it doesn’t crash and counts Wombles, but there are a few problems:
The Womble Count doesn’t appear until we click the button, and what happens when we rotate the screen?

Oh, that’s no good. The Womble Counter is being reset every time we rotate the screen.
Why is that?

Activity Destruction and Re-creation

One of the most troublesome parts of developing for Android is coping with the situation where your Activity is suddenly destroyed and re-created without warning.

The most common cause of this is screen rotation.

Some developers choose to disable screen rotation by locking their app into a single orientation, others try to suppress Activity destruction by “handling” the configuration change.

Both approaches are flawed.

For example. the recent introduction of display scaling may cause their applications to break because it causes activities to be destroyed and re-created the activity. Who knows what Google may introduce in the future?

It is much better to find a way of coping with this process safely.

The approach I take is to keep the Presenter and Model “alive” while the Activity is in the process of being renewed (destroyed and re-created).

To do this I place a reference to the presenter in a safe place just before the Activity is destroyed and retrieve it as it is being re-created:

  • Save your presenter in onSaveInstanceState(Bundle outState)
  • Restore it in onCreate(Bundle savedInstanceState) if savedInstanceState isn’t null

An important note about onSaveInstanceState is that it is only called if your Actvity is going to be re-created.
Likewise, the savedInstanceState Bundle will only be non-null if the activity being created is a re-creation of a previous activity.

Now at this point we hit another snag. onSaveInstanceState is expected to write our state to a bundle and onCreate is expected to read it from a Bundle.

Bundles aren’t good at containing complex binary objects and certainly can’t contain running code. So what do we do?

The Object Registry Saves the Day (and our Presenter)

I use a little utility class that I call the ObjectRegisty.

Its job is to hold on to our Presenter until we want it back.

Usually this is just long enough for our Activity to be destroyed and re-created.

We give it a reference to the Presenter and it gives us a “key” which happens to be a String. Very convenient to store in a bundle.

When we want our presenter back, we give the ObjectRegistry the key it gave us and it returns our Presenter.

It’s kind-of like a bag-check. You give them your bag, they give you a ticket. Later on you give them your ticket and they return your bag.

What does it look like?

/**
  * OnjectRegistry - the static map makes this a singleton in disguise, it will return the same object map for every
  * instance. Not a static utility class as normally you would inject this class and static classes are notoriously
  * awkward to mock for testing... Created by chrisjames on 15/3/17.
  */
 public class ObjectRegistry {
     private static Map mObjectMap = new HashMap(); 

     @Nullable
     public  RegistryT get(@NonNull String key) {
         UUID uuidKey = UUID.fromString(key);
         return (RegistryT) mObjectMap.remove(uuidKey);
     }

     @NonNull
     public String put(Object object) {
         UUID key = UUID.randomUUID();
         mObjectMap.put(key, object);
         return key.toString();
     }

 }

The sharp-eyed may have noticed the sneaky static declaration of the mObjectMap field.

This is how we keep our Presenter alive. Yes. I know. You have been told “statics are bad” and sometimes they are, especially when you can’t know when you should destroy them.

But in this case we expect the ObjectRegistry to live for the entire process lifespan and as long as you don’t abuse it, every object placed in the ObjectRegistry will be removed within the Application lifecycle.

So, we are going to use the ObjectRegistry to hold on to our Presenter and save the “Key” from the ObjectRegistry in our bundle onSaveInstanceState and read it back out again to retrieve our presenter in onCreate:

public class WombleCounterViewActivity
        extends AppCompatActivity
        implements WombleCounterContract.View {
    ...
    // Used to store the ObjectRegistry key for our presenter in the savedInstanceState bundle
    private static final String STATE_PRESENTER = "presenter";
    ...
    // The brains of the operation; our Presenter
    private WombleCounterContract.Presenter mPresenter;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    ...
        if (savedInstanceState == null) {
            // New instance of this activity, create our presenter and model
            WombleCounterContract.Model model = new WombleCounterModel();
            mPresenter = new WombleCounterPresenter(this, model);
        } else {
            // This activity has been re-created (maybe due to rotation) so we will have
            // saved our presenter in the object registry and put the resulting
            // key into the saved instance state.
            // Get the object registry
            ObjectRegistry objectRegistry = new ObjectRegistry();
            // Get the object registry key for the presenter out of the saved instance state
            String registryKey = savedInstanceState.getString(STATE_PRESENTER);
            // Retrieve our presenter from the object registry (this also removes it from the registry
            // so that we don't accumulate presenters every time we restore the activity
            mPresenter = objectRegistry.get(registryKey);
        }
    ...
    }
    ...
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        // onSaveInstanceState is called before this activity is destroyed but is going to be reconstructed
        // Get the object registry
        ObjectRegistry objectRegistry = new ObjectRegistry();
        // Put our presenter into the registry to save it, we get a key which we will
        // put into the saved instance state
        String registryKey = objectRegistry.put(mPresenter);
        // Put our registry key into the saved instance state
        outState.putString(STATE_PRESENTER, registryKey);
    }
    ...
 }

So, now what happens when we run and rotate our little Womble Counter?


What happened? When we rotated the screen the button stopped working?

Actually, the button is working fine.

The problem is we lost our View.

You might remember something I said earlier in this article:

“The obvious thing seems to be to pass it in the constructor. It actually isn’t the right thing to do for reasons we will discover later, but we will do it for now.”

We just discovered the reason.

We pass our View into our Presenter through its constructor.

But now we only construct the Presenter once, when we first start our Activity..

When the Activity is rebuilt, the reference held by the Presenter is set to null (weak reference, remember?) so the Presenter has no way of notifying the View of updates.

It seems we shouldn’t pass the View to the Presenter via the Constructor.

Connecting View and Presenter in a Rotating World

Actually, the solution is surprisingly simple. Instead of passing the View to the Presenter as a constructor parameter in onCreate, we pass it later on, during the onStart event. For that we need to update the Presenter interface in our Womble Counter Feature Contract and add a new member:

public interface WombleCounterContract {
    ...
    /**
     * MVP Presenter interface
     */
    interface Presenter {
        ...
        void bindMvpView(View view);
        ...
    }
    ...
}

We have added a new member named bindMvpView to our Presenter interface.

Its job will be to set the (weak) reference to the View in the Presenter.

Now we need to implement it:

public class WombleCounterPresenter
        implements WombleCounterContract.Presenter,
                   WombleCounterContract.ModelListener {
    ...
    private WombleCounterContract.Model mModel;
    private WeakReference mViewWeakReference;
    ...
    public WombleCounterPresenter(@NonNull WombleCounterContract.Model model) {
        mModel = model;
        mModel.setListener(this);
    }
    ...
    @Override
    public void bindMvpView(@Nullable View view) {
        mViewWeakReference = new WeakReference(view);
    }
    ...
}

Notice we also removed the View parameter from the constructor.

Now we have to call our new bindMvpView method:

public class WombleCounterViewActivity
        extends AppCompatActivity
        implements WombleCounterContract.View {

    @Override
    protected void onStart() {
        super.onStart();
        // Pass this MVP view to the presenter. The views may not be fully restored yet but that
        // doesn't matter because we will ask the presenter to refresh all of our views when we resume
        mPresenter.bindMvpView(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Tell the presenter we are not available. This prevents useless updates to our views (which can't be seen
        // or are actually unavailable
        mPresenter.bindMvpView(null);
    }
}

Okay, lets try it again:


Yay! This time rotation worked!

Our counter wasn’t reset and continued on with the correct number.

But it still doesn’t show the counter until we click the button. One more problem to solve.

 Is the room empty if you close your eyes?

So, there are actually a lot of ways we could solve this problem, but the one I will propose will solve quite a lot of problems (that I am going to ignore for now, you can discover them in your own time if you  want).

We are going to add yet another method to our Presenter interface in our Womble Counter Feature Contract.

This method instructs the Presenter to “re-draw” the View with the current state.

public interface WombleCounterContract {
    ...
    /**
     * MVP Presenter interface
     */
    interface Presenter {
    ...
        void refreshMvpView();
    ...
    }
    ...
}

We then implement this new member in our Presenter:

public class WombleCounterPresenter
        implements WombleCounterContract.Presenter,
                   WombleCounterContract.ModelListener {
    ...
    private WeakReference mViewWeakReference;
    ...
    @Override
    public void refreshMvpView() {
        // We have been asked to refresh the view with our state held in the model
        // Get our reference to the view (which *can* be null)
        WombleCounterContract.View view = mViewWeakReference.get();

        if (view != null) {
            view.displayWombleCount(mModel.getCurrentWombleCount());
        }
    }
    ...
}

Now we have to call this new method.

When do we call it? After the View has been passed to the Presenter and all the actual Android views are ready to be used.

When is that? In the onResume event of our Activity.

public class WombleCounterViewActivity
        extends AppCompatActivity
        implements WombleCounterContract.View {
    ...
    // The brains of the operation; our Presenter
    private WombleCounterContract.Presenter mPresenter;
    ...
    @Override
    protected void onStart() {
        super.onStart();
        // Pass this MVP view to the presenter. The views may not be fully restored yet but that
        // doesn't matter because we will ask the presenter to refresh all of our views when we resume
        mPresenter.bindMvpView(this);
    }
    ...
    @Override
    protected void onResume() {
        super.onResume();
        // Ask our presenter to refresh all of our views with the correct values from the Model
        mPresenter.refreshMvpView();
    }
    ...
}

You might be looking at that little code snippet and wondering why I don’t call it in onStart?

Well, here’s a little fun fact. The Android views may be still in the process of restoring their state during onStart.

In some cases it can result in the values you try to show in your presenter being immediately overwritten with values restored by the Android framework. It can lead to some very annoying and mysterious bugs.

Anyway. Let’s try it:

Well, that looks pretty good to me.

The Womble Count shows immediately, remembers the correct value and shows the Counter when rotated.

That is a working Android Model View Presenter application that correctly handles Activity destruction and re-creation.

So, as promised, here’s a link to the github project: Womble Counter Simple MVP Example

My apologies for this article being so long, but I felt it was important to take you through the journey so you could get a better understanding of how this evolved.

Next time: Threading.

Hint:

Everything in the View interface must run on the UI (main) thread, everything in the Presenter interface can run on any thread and everything in the Model interface should run on a background thread if it can’t return immediately (or at least very quickly).

Advertisements

2 thoughts on “A Simple MVP Approach for Android

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s