MVP and MVC

This is the third (and last) 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 2).

In the previous post of this series we saw how to extract UI logic into standalone classes and “hide” implementation details behind an interface. I mentioned that this abstraction of UI logic is, in my opinion, the most important aspect of MVC.

We also mentioned that there is no particular difficulty with implementation of MVC model in Android.

Therefore, in order to conclude our thorough review of MVC, the only component left for discussion is MVC controller.

MVC controller:

Each application has its own functionality which is achieved by careful definition of set of rules by developers. This set includes (but is not limited to) rules that define how application:

  • consumes inputs and produces outputs (data, events and interactions)
  • manages its internal state
  • handles device state changes (connectivity, location, orientation, etc.)
  • interacts with OS and other applications (taking pictures, setting alarms, etc.)

However, this set does not include the functionalities of MVC views and MVC models: both the way you present the output to the client and the way you store application’s internal state are implementation details. These details should not affect the above rules in any way, and, ideally, you should be able to switch to a different implementation at any time without modifying any of the rules.

The above set of rules, when captured in code, constitutes application’s business logic. In MVC, controllers are the components that encapsulate application’s business logic.

MVC controllers implementation:

As we discussed in part 1 of this series, Activities and Fragments are MVC controllers. If you still feel uncomfortable with this statement, you can read the article explaining why Activities in Android are not UI elements.

The general idea is that each MVC controller instantiates (or gets through injection) one or several MVC views and “offloads” UI related tasks to them, while executing application’s business login and “talking” to MVC model in order to update or query application’s state.

As an example, let’s review the implementation of SmsDetailsFragment:

/**
 * This fragment is used to show the details of a SMS message and mark it as read
 */
public class SmsDetailsFragment extends BaseFragment implements
        SmsDetailsViewMvc.ShowDetailsViewMvcListener,
        SmsMessagesManager.SmsMessagesManagerListener {

    /**
     * This constant should be used as a key in a Bundle passed to this fragment as an argument
     * at creation time. This key should correspond to the ID of the particular SMS message
     * which details will be shown in this fragment
     */
    public static final String ARG_SMS_MESSAGE_ID = "arg_sms_message_id";

    private SmsDetailsViewMvc mViewMVC;

    private SmsMessagesManager mSmsMessagesManager;

    private long mSmsMessageId;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        // in general, better use dependency injection library (e.g. Dagger 2) for members' init
        mSmsMessagesManager = new SmsMessagesManager(
                getActivity().getContentResolver(),
                getMainThreadPoster(),
                getBackgroundThreadPoster());

        // Get the argument of this fragment and look for the ID of the SMS message which should
        // be shown
        Bundle args = getArguments();
        if (args.containsKey(ARG_SMS_MESSAGE_ID)) {
            mSmsMessageId = args.getLong(ARG_SMS_MESSAGE_ID);
        } else {
            throw new IllegalStateException("SmsDetailsFragment must be started with SMS message ID argument");
        }

        // Instantiate MVC view associated with this fragment
        mViewMVC = new SmsDetailsViewMvcImpl(inflater, container);
        mViewMVC.setListener(this);

        /*
        Starting with API 19 (KitKat), only applications designated as default SMS applications
        can alter SMS attributes (though they still can read SMSs), therefore on post KitKat
        versions "mark as read" button is only relevant if this app is the default SMS app.
         */
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            String defaultSmsPackage = Telephony.Sms.getDefaultSmsPackage(getActivity());
            if (!getActivity().getPackageName().equals(defaultSmsPackage)) {
                mViewMVC.markAsReadNotSupported();
            }
        }

        // Return the root view of the associated MVC view
        return mViewMVC.getRootView();
    }

    @Override
    public void onStart() {
        super.onStart();
        mSmsMessagesManager.registerListener(this);
        mSmsMessagesManager.fetchSmsMessageById(mSmsMessageId);
    }

    @Override
    public void onStop() {
        super.onStop();
        mSmsMessagesManager.unregisterListener(this);
    }

    @Override
    public void onMarkAsReadClick() {
        mSmsMessagesManager.markMessageAsRead(mSmsMessageId);
    }

    @Override
    public void onSmsMessagesFetched(List<SmsMessage> smsMessages) {
        for (SmsMessage smsMessage : smsMessages) {
            if (smsMessage.getId() == mSmsMessageId) {
                mViewMVC.bindSmsMessage(smsMessage);
                return;
            }
        }
        Toast.makeText(getActivity(), "Couldn't fetch the SMS message of interest!", Toast.LENGTH_LONG).show();
    }

}

The main functionality of this controller is the following:

  1. Fetch data of a specific SMS message from the model
  2. Bind the fetched data to MVC view
  3. Mark the SMS message as “read” in model upon receiving notification about user interaction from MVC view

However, the important part is THE LOGIC THE CONTROLLER DOES NOT CONTAIN:

  1. MVC controller does not contain UI implementation details (i.e. no dependency on Android View class, no references to R.id.* identifiers, etc.) – we could introduce another implementation of SmsDetailsViewMvc, having a completely different UI, and switch to the new implementation by changing one line of code inside MVC controller (and it would be no changes at all if we’d used dependency injection framework or Factory design pattern).
  2. MVC controller does not contain implementation details of data storage mechanism – controller depends only on the contract of ContentProvider which is being queried.

Another important point to note is the usage of SmsMessagesManager class. This class encapsulates business logic related to SMS messages. SmsMessagesManager is also part of a “generalized” controller. Therefore, we shouldn’t necessarily put all the business logic into Activities and Fragments – controllers can be composite objects, and, in general, must adhere to SOLID principles. [Some people would claim that such “managers” are part of a model rather than part of a controller. I wouldn’t agree with them, but, as long as the code is clean, wouldn’t argue either.]

In terms of “dirtiness metrics” defined in Activities in Android are not UI elements post, the above implementation of controller scores 1 “dirtiness point” (due to dependency on SmsDetailsViewMvcImpl). We could get it down to 0 score by using dependency injection techniques (I didn’t want to complicate the code).

Introduction of MVC into codebase:

If you want to try MVC, you should understand that there will be difference between new projects written from scratch, and projects that already have some legacy code.

For new projects just start with MVC and stick to the principles. It will take some time to get accustomed to it, but in a matter of days you’ll learn the technique, and once you’ve got it – it will feel the most natural thing to do.

For existing projects I would recommend NOT doing “the big refactoring”. Don’t schedule weeks for long refactoring activities. Instead, each time you need to make a change to one of your Activities or Fragments, just convert them into MVC controllers one at a time (by extracting logic into MVC views and models). This will allow the project to keep the pace while being gradually refactored to a clean, decoupled and maintainable code.

Conclusion:

In this series of posts we discussed the general aspects of MVC and MVP architectural patterns, and provided an example of one possible implementation of MVP pattern (which we agreed to call MVC) for Android development.

Sometimes, due to a coupling introduced at Android OS level, it is not trivial to implement an ideal MVC. While the examples used in tutorial application are simple, complex UIs having rich functionality will also benefit from transition to MVC (in fact, the more complex the system, the bigger benefits you’ll get by MVC’ing it).

I also open-sourced a real application that uses MVC. You can review the source code of this application in order to see how MVC integrates into larger projects. Try to read the source code of several controllers and note how easy it is to understand what they do. Also use the code as a reference implementation for a more complicated UI constructs (lists, navigation drawer, toolbar, etc.).

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

This article has 18 comments

  1. Exerosis Reply

    I know it has been awhile since this was posted. However, I have a few questions if you are still around to answer them 🙂 So! I just started using MVC for the first time, and I think I can say I really like it! But there are a few things I have no idea how to properly implement. Namely how to handle fragments properly, in my app, I have one view that is capable of displaying a grid of images passed from my Controller(the fragment class). I would like to add a navdrawer to the project, that navdrawer should change what images are loaded, but preferably not switch out the fragment. That pushes me to say I need to have a Controller interface that my fragment can implement, that way when you go to create the fragment you just keep an instance and you can call .setImageProvider() or whatever. But this gets messy very quickly! The question is, how should I handle things like nav drawers, that should be app-wide on most devices, and yet need to communicate with their child fragment(s).
    Apologies on such a long post, I hope this makes a little sense 😛

    • Vasiliy Reply

      Hello and thank you for your question.
      So, basically, you want to affect the contents of various Fragments in your app from NavDrawer, right? This can be further generalized to a question of communication between two independent MVC Controllers in the application (e.g. Fragment displayed in Toolbar and Fragment displayed as main content).
      I see at least two possible approaches:
      1) The scheme you described is actually very close to the officially recommended approach for communication between Fragments. However, I think your analysis is correct and this kind of code increases system’s inter-coupling and is not easy to maintain in the long run.
      2) The better approach (IMHO) is to make use of event bus – “master” MVC Controller posts “update” events to event bus when user interactions occur, whereas “slave” MVC Controller registers itself to event bus in onStart() (unregisters in onStop()) in order to receive these events. Please note that I intentionally say “MVC Controller” and not “Fragment” – both the “master” and the “slave” could be Activities or Fragments (or even Services, which are not part of MVC at all). All my projects use Green Robot’s EventBus, and I can’t recommend it highly enough.

  2. Marija Reply

    Thank you so much for this! If it’s not a problem, could you please tell me how you MVCed the navigation drawer? I’m not sure what the right approach is for it.

  3. Damian Reply

    Hi Vasiliy. Thanks to your project I have finally understand the concept of the MVC pattern. But I have a questions regarding the structure of your sample project and structuring the future projects. So. In your project you named one of its root packages the “screens” package what implies that this package contains screens visible to the user. Does in this context placing the “common” package in the “screens” package where it is only a host for other screens is appropriate or not?

    Beside, after opening your sample project I misunderstood the concept of the “common” package. I thought that it contains elements used by packages of other screens and de facto it is true. It contains base elements (BaseFragment and ViewMvc) used in other screen packages. But as I mentioned above it is also a host for other screens. So, should not the “common” package be renamed? Or at least elements (BaseFragment and ViewMvc) should not be moved into other package outside the “common” and “screens” packages?

    • Vasiliy Reply

      Hi Damian, and thanks for your question.
      What you’re asking about is not strictly related to MVC, but to the general organization of project’s source code. When I come to think about it now, this topic definitely deserves a post by its own. The below explanation is just a brief summary of my approach, but I think it will be enough for you because you got most of it by yourself already.
      Top level packages reflect application’s “purpose”. For any application of considerable size, most of top level packages will borrow their names from the “business/problem domain”. In case of MVC tutorial app, which is SMS reader, there is a top-level package named “sms”. This package contains SmsMessage and SmsMessagesManager classes. In addition, there is top-level package named “common”. This package contains classes and sub-packages that can be used by multiple other top-level packages (therefore “common”).
      Now, it could be very convenient to associate screens (which are view-controller pairs with related logic) with the aforementioned top level packages. In practice, however, one screen can depend on multiple top level packages. The tutorial app is very simple, but imagine that we add messages aggregation by contact. Then we would add “contacts” package and “smsall” screen would use both “sms” and “contacts” packages. Therefore, screens can’t be associated with existing top level packages. This is the reason we put them as sub-packages in a dedicated top-level package named “screens”. But different screens can share logic (e.g. base classes). I put this common logic related to screens into “common” sub-package of “screens” package.
      Let’s summarize. Top level packages reflect application’s “purpose”. Ideally, you should be able to understand what business/problem domain the application targets just by reading the names of the top level packages. In addition, there will be “common” and “screens” top level packages. Each application’s screen will be represented by a sub-package in “screens” top-level package. In addition, there will “common” sub-package in “screens” for logic that is shared between multiple screens. I hope this clarifies the structure a bit.
      * I updated the tutorial application in order to reflect the structure described above.

      • Damian Reply

        Thanks for your quick replay and clarification. If you do not mind I would be grateful If you validate or not my understanding. Suppose, that your SMS Reader application provides a settings screen. And it allows to set different options of settings for contact and SMSs. According to your explanation. The “screens” top-level package should contain the “contactssettings” and “smssettings” sub-packages that will represent application’s screens. I know that the both of the settings screens must have their own controllers in their “controller” sub-packages of their packages. Moreover, the “controller” package in the “common” sub-package in the “screens” top-level package should contain a class (e.g SettingsActivity) that provides the same logic and it is also the host for “contactssettings” and “smssettings” screens as the MainActivity class for “smsall” and “smsdetails” screens. But I want the “contactssettings” and the “smssettings” screens logic to be separated because I want them to be managed as a separated android task or just by design. And the top-level package named “settings” (containing class providing interface to write/read application preferences) should be added to application’s project structure that will be used by “contactssettings” and the “smssettings” screens packages. Is my understanding correct?

        • Vasiliy Reply

          Your understanding is correct.
          Few additional points to consider in respect with your specific example:

          1) Decide whether “settings” is a first class functionality of the app. If it is, then additional top-level package is appropriate. If it isn’t, then I’d put “settings” package into “common” top-level package (and, in fact, this is exactly what I did in IDoCare app)

          2) If “contactssettings” and “smssettings” are similar in functionality, then it might be better to put them in a single sub-package inside “screens”. Then, SettingsActivity could reside in this package too. This way you’ll have all settings screens related functionality in a single package. Both approaches are good, though, therefore it is more a matter of preference.

          3) If you want settings screens to run as different tasks, then you’ll need a separate Activity for each of them. This is not exactly analogous to MainActivity and “sms” screens which are implemented using so called “single Activity” approach.

          4) If you have both Activities and Fragments in some sub-package of “screens”, you can split “controllers” package into “activities” and “fragments” packages for readability. This is, again, a matter of personal preference.

  4. Kike Bodi Reply

    Thanks this great tutorial.

    I’m missing something, I don’t see exactly where we have to implement the model as we implement the Controler at the SmsDetailsFragment and the View at ViewMvc, SmsDetailsViewMcv and SmsDetailsViewMcvImpl.

    Is it SmsMessagesManager the model or it’s SmsDetailsViewMcvImpl?

    • Vasiliy Reply

      Hi,
      The state that is being dispalyed is in the database of SMS messages which is not part of this application at all. Therefore, the model in this case is external to the application and is being accessed via ContentProvider.

      Whether SmsMessagesManager is part of a model or a controller is kind of debatable. I prefer to see it as part of the controller because it does not have a state by itself. Some people find it conceptually easier to see it as part of the model. As I said in the article, it doesn’t really matter – as long as this component is there and provides the required level of abstraction and encapsulation, call it whatever you like.

  5. Damian Reply

    Hi, I have question regarding the ViewPager. In RootViewMvcImpl I inflate rootView from layout where the ViewPager is container for framgents. Is it correct to set OnPageChnageListener on the instance of viewPager and notifying the controller (Activity) about the currently displayed fragment through the listener provided by the RootViewMvc? And what about the adapter for ViewPager. Should it be set in RootViewImpl? I think not. I think RootViewImpl should provide method for controller to set FragmentStatePageAdapter on ViewPager.
    Is my understanding correct? Are other ways to implement ViewPager correctly in the context of MVC?

    • Vasiliy Reply

      Hello Damian,
      As you discovered, ViewPager for Fragments is going to be a bit dirty. Let me share a few thoughts on this subject from my own experience:

      1)
      Do you really need to use Fragments? IMHO, there are two uses cases for ViewPager with Fragments: a) each page represents independent and rich functionality (e.g. page1 is home, page2 is search, page3 is favorites, etc.) b) memory considerations require usage of FragmentStatePagerAdapter.
      In practice, however, many ViewPager’s don’t really need to work with Fragments and can work with MVC views (or even Android Views).

      2)
      If you use ViewPager with Fragments, there is no need to notify the host Activity about which Fragment is being shown. Fragments are self-sufficient, and if communication with host Activity is required – you can take care of it inside page Fragments themselves. If you think that there is a need in your case, it is an indication that you probably don’t need Fragments and MVC views will suffice.

      3)
      For the implementation of Fragment based ViewPager I suggest the following: have a separate Fragment that hosts only the ViewPager. Lets call it PagerHostFragment. Instead of using MVC, PagerHostFragment uses the old “spaghetti” approach. Put all the logic that manages page Fragments inside PagerHostFragment. In the host Activity, put PagerHostFragment in place where ViewPager is required (either in XML, or by adding PagerHostFragment into FrameLayout). PagerHostFragment is a bit “dirty”, but this dirtiness is encapsulated and will not spread out.

      To summarize: there are cases where it is ok to have some “dirtiness points”, as long as this is a calculated trade-off and the dirtiness is encapsulated inside a single component.

  6. Denis Ivashkov Reply

    Hello Alexey, and thanks for this great tutorial!

    Really. I am a begginner on Android and have been trying to understand the topic for a week now.
    Every other tutorials I’ve found so far were either to abstract, focusing only on high level explanation,
    or had terrific amount of hard-to-understand code examples.
    So cool I found your article, can’t thank you enough. It is so clear and intuitive as I suppose it should be.

    However, there are still some concepts I can’t catch on. Like for example:
    This is the chain of events happening after user taps a “Mark as read” Button (correct me if I’m wrong):

    1) onClick(View view) method of the mBtnMarkAsRead’s OnClickListener object is being called

    2) In it’s body mListener.onMarkAsReadClick() is being called

    3) In it’s body mSmsMessagesManager.markMessageAsRead(mSmsMessageId) is being called

    4) In it’s body the data is being updated (the sms value “read” is being changed to “true”)

    That’s it. It looks like this is the end.

    So my question is how th UI is updated after user clicks mBtnMarkAsRead ?
    I mean, it does not updates the appearence immediately after (like inside of the mBtnMarkAsRead’s onClick(View view) method).
    It seems the only place where this is happening is in the bindSmsMessage(SmsMessage smsMessage) method body. This method is called by the onSmsMessagesFetched(List smsMessages) method of the controller.
    And I can’t see where this method is being called in the chain of events after the mBtnMarkAsRead is being clicked which I described previously.
    In other words I don’t understand how the View is notified it has to change the appearence of the message representation after it is marked as read.

    Would ge great to hear your explanation. Again, thank you for this tutorial – it’s a grace.

    • Vasiliy Reply

      Hello Denis,
      Thanks for your feedback – I appreciate it very much.
      Indeed, the current implementation does not notify the view about changes. This is a bug. I already fixed it and pushed the fix into the repo.

      This bug was introduced when I refactored the implementation from Loaders to the current approach.
      One of the features of CursorLoader is that it automatically registers ContentObserver for the data it fetches, which allows for change notifications to be delivered without additional effort. When I ditched Loaders I forgot to implement this functionality myself.

      In order to keep the code simple, I did not implement a full blown ContentObserver scheme, but just added logic in SmsMessagesManager that re-fetches all the SMS messages and notifies listeners after the shown message is marked as read.

      This is yet another example of why unit-testing is an essential practice in software development. I did not unit test this code in order to keep the repo simple, but time after time lack of unit tests bites me in the a**.

      Thank you very much for catching this bug and taking time to report it.

      P.S. My name is Vasiliy )

  7. Denis Ivashkov Reply

    Hello again Vasiliy,

    I’m trying to implement what I’ve learned from your tutorial in my own simplest MVC example.
    So far it is going well, however questions arrise from time to time. Like:

    1) In the MainActivity class in the replaceFragment(…) method you use view ID in the method
    ft.replace(R.id.frame_contents, …)

    Doesn’t it break the single responsibility principle since now activity becomes dependent on the UI component?
    I understand the neccecity of this: we have to tell fragment manager what view to populate with our new fragment.

    Wouldn’t something like mViewMVC.getRootView().getId() instead of R.id.frame_contents o the trick?
    I tried to do this replacement and launch the app: nothing seems to be broken so far.
    Are there any hidden pitfalls of this replacement?

    2) What about BackgroundThreadPoster and MainThreadPoster classes – why should they belong to the MvcTutorialApplication class rather than to MainActivity? What is the value of this?

    And one more about posters. I’m new to these concepts, could you please tell me: if my storage is going to be a web-server, and I’m going to use Retrofit, do I still need these? Since retrofit can on it’s own make sync and async operations without involving additional agents?

    3) It seems SmsMessagesManager is a concrete class, which extends BaseObservableManager so it could have functionality of registering and unregistering listeners (and get access to them).
    Am I right, that in the ideal spherical cow case it also should implement some sort of generic interface like this:

    public interface ISmsMessagesManager {

    fetchSmsMessageById(final log id);
    fetchAllSmsMessages();
    markMessageAsRead(final long id);
    notifySmsMessagesFetched(final List smsMessages);
    }

    So it’s implementations could be easily changed if our storage would change for example from internal storage to database or cloud-server?

    Again, thanks for your work, after careful reading through your code I consider it to be the cleanest code I’ve ever read so far.

    • Vasiliy Reply

      Hello Denis,
      These are very good questions.

      1) Yes, a reference to ID of the frame is indeed 1 “dirtiness point” as defined here. In IDoCare I mitigated the problem by abstracting all these dirty details into MainFrameHelper class, but if your solution works – it might be even better!

      2) Thread posters are my invention – these classes allow for easy and unit-testable multithreading. I need to find a time to wrap them into a library and present to the world. In general, multithreading is hard, and the associated bugs are the most difficult to find and fix. If your application doesn’t need special multithreading and you can offload it to the libraries (e.g. Retrofit) – that would be the best.

      3) Interfaces are powerful tools, but they should be used cautiously.
      If you already have multiple implementations, then sure – extract an interface for them. In this case, however, there is just one implementation, and this code is not very complicated. The value of an interface will be questionable, but each time a method will need to be added/removed you’ll have to make changes in 2 places.
      Therefore, in this case, I would say that interface is more of a burden. However, this is more of a taste issue than anything, so if you like having clear boundaries between classes – go on and extract interfaces for the managers.
      I already forsee your next question – why did I define interfaces for MVC views then?
      For this simple tutorial application it is also more of a burden, but I wanted to show the most general approach. In real applications, MVC views can be hundreds lines of code in length and have tens public methods (e.g. onClick()) – a clear interface allows for easy understanding of the contract, without a need to read the actual implementation.
      By the way, on one project I worked on we decided not to extract these interfaces and it also worked out very well, therefore this is also more of a taste thing.

      Thanks for the kind feedback – it really feels good to know that my ideas are of use to people 🙂

Leave a Comment

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