MVP and MVC

This is the second post in series of three posts that discuss Model View Controller (MVC) and Model View Presenter (MVP) architectural patterns in context of Android development (part 1, part 3).

MVP in Android:

In part 1 of this post we concluded that MVP architectural pattern is more suitable for Android development due to very tight coupling between Activities and Fragments (which are MVP presenters) and various parts of Android framework.

However, I don’t like the term “presenter”. “Controller” sounds like it is in control of something, but “presenter”… In fact, in context of our definitions, MVP presenter has more responsibilities than MVC controller. Furthermore, since the terms themselves are ambiguous, many programmers don’t make any distinction between the two architectural patterns – why bother? I tend to agree with those who discard the differences, and also call both architectural patterns MVC.

Therefore, we are going to see one possible implementation of MVP architectural pattern in Android, but, starting now, I’m going to free myself from terminological accuracy and call it MVC. IMHO, controller is much clearer term than presenter.

As I said earlier, there is no special complexity associated with implementation of model in Android. You can write your own ContentProvider, or, if it is too complex for your needs, you can implement a standalone application’s model (using e.g. Singleton design pattern injected implementation of some ModelMvc interface). Since there is nothing special about models in Android, in the tutorial application I decided to use an existing external ContentProvider as a model in order to be able to concentrate on the tricky parts of MVC in Android. If you need a good tutorial on ContentProviders – Wolfram Rittmeyer wrote a great series of posts about it.

In this part of the post we are going to discuss MVC views in general, and how they can be implemented in Android. However, you might be wondering whether Android View class has anything to do with MVC views, so let’s begin by making our terminology unambiguous.

Are MVC view and Android View synonyms?

Android framework already contains a component called View – it is a base class for all UI building blocks. While there are some similarities between Android Views and MVC views, they are not the same. The relationship between the two can be described this way:

  • In general, Android View is not MVC view.
  • For each MVC view, there must be a single Android View which represents MVC view’s “user visible” part (the “root” Android View of UI hierarchy).
  • MVC view can contain nested MVC views.

It might be a bit confusing right now, but the examples provided later will put the above statements into meaningful context.

Since we got this ambiguity in respect to the meaning of “view”, I shall explicitly mention the type of “view” I’m referring to – it will be either “Android View”, or “MVC view”. Keep in mind that when I say Android View, I mean instances of either View class itself, or any of its sub-classes (widgets, layouts, etc.).

How do I implement MVC views?

So, how do we define MVC view? In the tutorial app, MVC view is any class which implements ViewMvc interface (the javadoc is self explanatory):

/**
 * MVC view interface.
 * MVC view is a "dumb" component used for presenting information to the user.<br>
 * Please note that MVC view is not the same as Android View - MVC view will usually wrap one or
 * more Android View's while adding logic for communication with MVC Controller.
 */
public interface ViewMvc {

    /**
     * Get the root Android View which is used internally by this MVC View for presenting data
     * to the user.<br>
     * The returned Android View might be used by an MVC Controller in order to query or alter the
     * properties of either the root Android View itself, or any of its child Android View's.
     * @return root Android View of this MVC View
     */
    public View getRootView();

    /**
     * This method aggregates all the information about the state of this MVC View into Bundle
     * object. The keys in the returned Bundle must be provided as public constants inside the
     * interfaces (or implementations if no interface defined) of concrete MVC views.<br>
     * The main use case for this method is exporting the state of editable Android Views underlying
     * the MVC view. This information can be used by MVC controller for e.g. processing user's
     * input or saving view's state during lifecycle events.
     * @return Bundle containing the state of this MVC View, or null if the view has no state
     */
    public Bundle getViewState();

}

Note that this interface is very general and lightweight, therefore any existing class can be easily extended or wrapped in order to be used as MVC view. On the other hand, this interface does not have any means for differentiating between different MVC views – each concrete MVC view will need to be defined as sub-interface of ViewMvc, and MVC controllers will depend on these sub-interfaces. This dependency does not violate MVC paradigms – controllers need to depend on particular MVC views, though this dependency should be constrained to dependency on just the interface.

Let’s take a closer look at the interface of one of the MVC views in the tutorial appSmsDetailsViewMvc:

/**
 * This interface corresponds to "details" screen of the app, where details of a single SMS
 * message should be displayed
 */
public interface SmsDetailsViewMvc extends ViewMvc {

    interface ShowDetailsViewMvcListener {
        /**
         * This callback will be invoked when "mark as read" button is being clicked
         */
        void onMarkAsReadClick();
    }

    /**
     * Hide "mark as read" button
     */
    void markAsReadNotSupported();

    /**
     * Show details of a particular SMS message
     * @param smsMessage a message to show
     */
    void bindSmsMessage(SmsMessage smsMessage);


    /**
     * Set a listener that will be notified by this MVC view
     * @param listener listener that should be notified; null to clear
     */
    void setListener(ShowDetailsViewMvcListener listener);

}

The code should be self-explanatory. Please note the callback interface – MVC controllers which use this MVC view will need to implement that interface and register themselves with the MVC view in order to be notified about user’s actions.

The actual functional class that implements SmsDetailsViewMvc interface is SmsDetailsViewMvcImpl:

/**
 * An implementation of {@link SmsDetailsViewMvc} interface
 */
public class SmsDetailsViewMvcImpl implements SmsDetailsViewMvc {

    private View mRootView;
    private ShowDetailsViewMvcListener mListener;
    private boolean mMarkAsReadSupported = true;

    private TextView mTxtAddress;
    private TextView mTxtDate;
    private TextView mTxtBody;
    private Button mBtnMarkAsRead;

    public SmsDetailsViewMvcImpl(LayoutInflater inflater, ViewGroup container) {
        mRootView = inflater.inflate(R.layout.mvc_view_sms_details, container, false);

        initialize();

        mBtnMarkAsRead.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mListener != null) {
                    mListener.onMarkAsReadClick();
                }
            }
        });
    }

    private void initialize() {
        mTxtAddress = (TextView) mRootView.findViewById(R.id.txt_sms_address);
        mTxtDate = (TextView) mRootView.findViewById(R.id.txt_sms_date);
        mTxtBody = (TextView) mRootView.findViewById(R.id.txt_sms_body);
        mBtnMarkAsRead = (Button) mRootView.findViewById(R.id.btn_mark_as_read);
    }

    @Override
    public void markAsReadNotSupported() {
        mMarkAsReadSupported = false;
    }

    @Override
    public void bindSmsMessage(SmsMessage smsMessage) {
        mTxtAddress.setText(smsMessage.getAddress());
        mTxtDate.setText(smsMessage.getDate());
        mTxtBody.setText(smsMessage.getBody());

        mRootView.setBackgroundColor(smsMessage.isUnread() ?
                mRootView.getResources().getColor(android.R.color.holo_green_light) :
                mRootView.getResources().getColor(android.R.color.white));

        mBtnMarkAsRead.setVisibility(smsMessage.isUnread() && mMarkAsReadSupported ?
                View.VISIBLE : View.GONE);

    }

    @Override
    public void setListener(ShowDetailsViewMvcListener listener) {
        mListener = listener;
    }

    @Override
    public View getRootView() {
        return mRootView;
    }

    @Override
    public Bundle getViewState() {
        return null;
    }

}

As you can see, this MVC view is very simple, which is always an advantage. Let’s understand what’s going on here:

  • Constructor: the main aspect of MVC views’s construction is initialization of underlying Android “root” View. Usually you will do that by inflating it from XML layout file. Once root Android View is initialized, you will proceed with initialization of other Android Views contained in the inflated hierarchy. In this case, we simply obtained references to hierarchical Android Views that we will use, and registered a click listener with one of them.
  • getViewState(): this MVC view does not capture user’s input (except for button clicks), therefore it does not have any state that MVC controllers could be interested in. In such cases, getViewState() method should return null.
  • bindSmsMessage(SmsMessage): this MVC view presents data to the user. In this case, the data comes from MVC model and it is MVC controller’s responsibility to fetch it (MVC views aren’t aware of model’s existence, remember?). Controller will invoke this method in order to bind the fetched data to this MVC view.
  • setListener(): register a listener with this MVC view that should be notified of user’s actions.

Please note, that MVC views don’t depend on Activity or Fragment – they can be used with either of them, or even be instantiated inside other MVC views. This takes the idea of reuse of UI elements to the next level and makes any subsequent UI changes way more efficient and less error-prone. Therefore, the next time UX/UI designers asks you to change the appearance of the application, all you need to do is provide different implementations of your MVC views.

But the real advantage of this approach will be felt by those developers, who unit-test their code (I will definitely write many posts on this subject) – you can now easily mock the UI of your application.

There is, however, some cases in which implementation of MVC views becomes a bit more tricky. One of the use cases that require some additional work is when we’d like to implement MVC view that represents an element in ListView. The complication arises due to the fact that sub-classes of Adapter have very strong dependency on Android View. For the sake of simplicity I use an approach which resembles ViewHolder pattern: SmsAllListAdapter instantiates MVC views, gets their root Android Views, and then sets MVC views as tags of Android Views. This way the adapter can obtain a reference to MVC view by casting the tag of the corresponding Android View. I don’t like this approach too much, but it works nicely and does not require much code.

In terms of “dirtiness metrics” for UI logic defined in Activities in Android are not UI elements post, the above implementation of MVC view scores 0 “dirtiness points” – it has no direct or transitive dependencies on either Activity or Fragment!

Conclusion:

In this post we saw one possible implementation of MVC view – a standalone UI container which is responsible for presenting application’s output to the client and delivering client’s input to a general object which implements listener interface (this object could be either MVC controller, or another MVC view). MVC views are independent of Activities and Fragments, therefore they can be easily reused, updated or completely replaced, without affecting any existing business logic.

In the next (and last) part of this post we’ll take a look at implementation of MVC controllers in Android.

Please leave your comments and questions below, and consider subscribing to our newsletter if you liked the post.

This article has 13 comments

  1. Deadloque Reply

    Hi, and thank you for this great tutorial, I’m learning a lot, but I’m still a little confused. I don’t understand why you’re saying “MVC views aren’t aware of model’s existence” but the view clearly has a direct dependency on the SmsMessage class, which is part of the model, no ?

    • Vasiliy Reply

      Hi. I appreciate your feedback and question. When I say “model”, I refer to the entity that stores the state of the application (whether this state persistent or not). Objects of SmsMessage class do not store application’s data – they only mirror specific parts of that data in order to be able to pass these parts across application’s layers. Some people call them “Value Objects (VA)”, others call them “Plain Old Java Objects (POJOs)”. There are people (and frameworks) who refer to these objects as “Models”, “View-Models”, “Transactional Models”, etc. As I mentioned in part 1 of this post, MVC is very ambiguous abbreviation which has no widely accepted definition, therefore you can call SmsMessage “Model”, but then substitute each occurrence of “model” with “application’s datastore” (or whatever term you’re comfortable with) when reading these posts.

      • Alexey Reply

        As I understand, view should get only simple data on the input (setMessageText(String); setDate(String)), because controller should decide how to show data. May be, it will be needed to display message’s date in format depend business logic.

        • Vasiliy Reply

          Hello Alexey. Views indeed should get only simple data and POJOs as inputs, and it is view’s responsibility to “decide” HOW to show that data to the user. The responsibility to “decide” WHAT data should be shown belongs to controller.

          • Alexey

            Vasiliy, it’s nice to see the quick response. From where does the view get data to decide how to display it? Imagine the situation: in a group dialog you should display the date in one format and in a single dialog the date should be displayed in other format. How will the view know about the dialog type? For my opinion, this is responsibility of controller.
            But, in most things, your approach is very proper for my opinion. When I started to see into MVC, this thought was the first that visited me. But I am very tired to dispute with my colleagues that activity and fragment should be as a controller or a presenter. They do not want to hear anything. I will write the response after reading the third part. Thank you.

  2. Sushil Kadu Reply

    I understood every single line you wrote. Thank you for such lovely blog.

  3. Pablo Reply

    Thanks for the great lesson. While trying to apply this architecture, I ran into a problem: say I decide to swap the SMS Details view for another one and my boss insists the “mark as read” button must be a button (uncollapsed menu item) in the appbar. How do I do this without messing with the Controller/Presenter?

    • Vasiliy Reply

      Hello and thank you for your question. If such a need arises, you could add CoordinatorLayout and AppBarLayout into mvc_view_sms_details.xml layout file in a usual manner. Then, in the same layout file, add the button to the AppBar and remove the currently used button. Then, in SmsDetailsViewMvcImpl, remove the reference to the current button and register the same listener with the button that is now located in the AppBar. You are done now, and neither the interface of the MVC view, nor the respective controller were affected.

    • Vasiliy Reply

      Hi. This method was renamed to bindSmsMessage(SmsMessage). I edited the post. Thanks for catching this!

Leave a Comment

Your email address will not be published. Required fields are marked *