Understanding MVC, MVP, MVVM patterns in Android

MVC (Model View Controller)

This is one of the most widely used approaches in software development. The MVC consists of three main components:

  • Model. The model represents the object in the application. This has the logic of where the data is to be fetched from. This can also have the logic by which the controller can update the view. In Android, the model is mostly represented by object classes.
  • View. The view consists of the components that can interact with the user and is responsible for how the model is displayed in the application. In Android, the view is mostly represented by the XML where the layouts can be designed.
  • Controller. The controller acts as a mediator between the model and the view. It controls the data flow into the model object and updates the view whenever data changes. In Android, the controller is mostly represented by the activities and fragments.

All of these components interact with each other and perform specific tasks, as shown in the following figure.

android_mvc.png

It has been noticed that Android is not able to follow the MVC architecture completely, as activity/fragment can act as both the controller and the view, which makes all the code cluttered in one place. Activity/fragment can be used to drawmultiple views for a single screen in an app, thus all different data calls and views are populated in the same place. Therefore, to solve this problem, we can use different design patterns or can implement MVC carefully by taking care of conventions and following proper programming guidelines.

The following shows a basic example of how MVC is used in Android.

Model.

public class LocationItem {
    String name;
    String address;
    public LocationItem(String name, String address) {
        this.name = name;
        this.address = address;
    }
    public String getName() {
        return name;
    }
    public String getAddress() {
        return address;
    }
}

View.

<TextView
    android:id="@+id/tvName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

Controller.

LocationItem item = new LocationItem("Name", "Address");

TextView tvName = (TextView) findViewById(R.id.tvName);
tvName.setText(item.getName());

The model here is LocationItem, which holds the name and address of the location. The view is an XML file, which contains a TextView, through which the name of the location can be displayed. The activity, which is the controller, contains a LocationItem. It gets the name of the LocationItem sets it up in the view, which displays it.

MVP (Model View Presenter)

Model View Presenter (MVP) is derived from the MVC pattern. MVP is used to minimize the high dependency on the view, which is the case in the MVC. It separates the view and model by using the presenter. The presenter decides what should be displayed on the view.

  • Model. The model represents the objects in the application. This has the logic of where the data is to be fetched from.
  • View. The view renders information to users and contains a UI Component .xml file, activity, fragments, and dialog under the View Layer. It does not have any other logic implemented.
  • Presenter. The presenter layer performs the task of the controller and acts as the mediator between the view and model. But unlike the controller, it is not dependent on the view. The view interacts with the presenter for the data to be displayed, and the presenter then takes the data from the model and returns it to the view in a presentable format. The presenter does not contain any UI components; it just manipulates data from the model and displays it on the view.

The interaction between the various components of the MVP are shown in the following figure.

android_mvp.png

In the MVP design, the presenter communicates with the view through interfaces. The interfaces are defined in the presenter class, to which it passes the required data. The activity/fragment or any other view component implements the interfaces and renders the data in a way they want. The connection between the presenter and the view is one to one.

In this example, we will show how the MVP design can be used to display a list into a RecyclerView.

public interface LocationInteractor {
    interface OnLoadFinishedListener {
        void onSuccess(List<LocationItem> items);
        void onFailed();
    }
    void loadLocations(OnLoadFinishedListener listener);
}

Here we have a LocationInteractor class that is used by the presenter to communicate with the model

public class LocationInteractorImpl implements LocationInteractor {
    @Override
    public void loadLocations(final OnLoadFinishedListener listener) {
        RetroInterface.getRssFeedApi().getFeed("", new Callback<LocationResponse>() {
            @Override
            public void success(LocationResponse locationResponse, Response response) {
                if (listener != null) {
                    if (locationResponse != null) {
                        listener.onSuccess(locationResponse.getDetails());
                    } else {
                        listener.onFailed();
                    }
                }
            }

            @Override
            public void failure(RetrofitError error) {
                if (listener != null) {
                    listener.onFailed();
                }
            }
        });
    }
}

This is the LocationInteractorImpl class which is the model. This class interacts with the presenter to provide the list of locations. The loadLocations function is used by the presenter to interact with the model and fetchthe list of locations.

The model (LocationInteractorImpl) then uses the listener.onSuccess and listener.onFailed methods to send results back to the presenter:

public interface LocationInterface {
    void showProgress();
    void hideProgress();
    void locationLoaded(List<LocationItem> items);
}

The LocationInterface class is used by the presenter to communicate with the view:

public interface LocationPresenter {
    void loadLocations();
    void onDestroy();
}

The LocationPresenter class is used by the view to communicate with the presenter. Depending on the functions called by the view, the presenter will communicate with the model and get the responses:

public class LocationPresenterImpl implements LocationPresenter, LocationInteractor.OnLoadFinishedListener {
    private LocationInterface locationInterface;
    private LocationInteractor locationInteractor;

    public LocationPresenterImpl(LocationInterface locationInterface) {
        this.locationInterface = locationInterface;
        this.locationInteractor = new LocationInteractorImpl();
    }

    @Override
    public void loadLocations() {
        if (locationInterface != null) {
            locationInterface.showProgress();
        }
        locationInteractor.loadLocations(this);
    }

    @Override
    public void onDestroy() {
        locationInterface = null;
    }

    @Override
    public void onSuccess(List<LocationItem> items) {
        if (locationInterface != null) {
            locationInterface.locationLoaded(items);
            locationInterface.hideProgress();
        }
    }

    @Override
    public void onFailed() {
        if (locationInterface != null) {
            locationInterface.locationLoaded(null);
            locationInterface.hideProgress();
        }
    }
}

The LocationPresenterImpl class is the presenter class which communicates between the view and the model. This class implements LocationPresenter with which the view communicates with the presenter and the LocationInteractor.OnLoadFinishedListener from which the model communicates with the presenter.

When the view calls the loadLocations function of the presenter, the presenter interacts with the model and calls the loadLocations method of the model, indicating that the model is to return the list of locations to be displayed.

The onSuccess and onFailed functions are then called by the model after the list has been fetched successfully or has failed. Then the presenter communicates with the view through the locationLoaded function. Here, it passes the list that has been fetched by the model.

public class LocationListActivity extends Activity implements LocationInterface {
    private ProgressBar progressBar;
    RecyclerView recyclerView;
    List<LocationItem> items;
    AddPlacesAdapter adapter;
    private LocationPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        progressBar = (ProgressBar) findViewById(R.id.progress);
        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        items = new ArrayList<>();
        adapter = new AddPlacesAdapter(this, items);

        adapter.setClickListener(new AddPlacesAdapter.ClickListener() {
            @Override
            public void onItemClickListener(View v, int pos) {
                // handle click events
            }
        });

        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapter);
        presenter = new LocationPresenterImpl(this);
        presenter.loadLocations();
    }

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

    @Override
    public void showProgress() {
        progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideProgress() {
        progressBar.setVisibility(View.GONE);
    }

    @Override
    public void locationLoaded(List<LocationItem> items) {
        if(items!=null) {
            this.items = items;
            adapter.refresh(items);
        }
    }
}

The LocationListActivity is the view where all the interaction with the user will happen. The view implements the LocationInterface, by which it gets the responses from the presenter. The view uses an instance of the presenter.

presenter = new LocationPresenterImpl(this);

It then calls the presenter.loadLocations() to tell the presenter that it wants the list of locations to be displayed.

MVVM

MVVM stands for Model-View-View-Model. It is similar to the MVC model, the only difference being it has two-way data binding with the view and view-model. The changes in the view are being propagated via the view-model, which uses an observer pattern to communicate between the view-model and the model. The view in this case is completely isolated from the model.

The major advantage of using MVVM is it enables automatic changes in the view via the view-model:

android_mvvm.png

MVVM has the following components:

  • Model. The model represents the objects in the application. This has the logic of where the data is to be fetched from.
  • View. The view is similar to the MVC pattern view, which renders information to users and contains a UI Component .xml file, activity, fragments, and dialog under the View Layer. It does not have any other logic implemented.
  • View-model. The view-model helps in maintaining the state of the view and does changes to the model based on the inputs gained from the view.

Many views can be linked to one view-model, which creates a many-to-one relationbetween the view and a view-model. Also, a view has information about the view-model but the view-model does not have any information about the view. The view is not responsible for the state of information; rather, that is being managed by the view-model and the view and the model are only reflected via the changes made to the view-model.

In developer's terminology, one can say that the view-model is the interface between a designer and a coder.

comments powered by Disqus