Model-View-Presenter Architecture in Android Applications

MVP_model_blogpost_illustration@2x

Recently, clean architecture has been a hot subject in the Android development world. As developers, our responsibility is not only limited to delivering apps with a bunch of features, we also need easily maintainable code with clear separation between presentation and business logic.

In this post, we’d like to illustrate the usage of the Model-View-Presenter architectural pattern as a method for developing Android apps with a clean architecture approach.

For demonstration purposes, we created a simple application for room booking. You can find the code for this app on our GitHub under RoomBookerMVP.

Architecting Clean Applications

As described on the 8thlight blog, well-architected software should be divided into layers for responsibility separation.

clean_arch

When applied, the Clean Architecture approach produces software that is:

  • Independent of frameworks,
  • Testable,
  • Independent of UI,
  • Independent of databases,
  • Independent of any external agency.

“Typical” Android Architecture: Model-View

A lot of Android applications use only the Model-View architecture. Using this approach, we can quickly develop new features. We use Activity or Fragment as an object that implements logic, handles UI, and controls the flow between data objects and UI. One typical scenario for creating a new screen entails creating a new Activity class, adding a bunch of views, some AsyncTasks for background operations, tracking the state of Activity… before realizing that we’ve ended up with complex code where everything is connected to everything.

Better: Model-View-Presenter

diagram_2

As you can see in the diagram, we added a Presenter layer between the Model and View components. The Model-View-Presenter (MVP) architecture comprises:

  • Model: the data layer,
  • View: the UI layer, displays data received from Presenter, reacts to user input. On Android, we treat Activities, Fragments, and android.view.View as View from MVP.
  • Presenter: responds to actions performed on the UI layer, performs tasks on Model objects (using Use Cases), passes results of those tasks to Views.

What we want to achieve by using MVP are simpler tasks, smaller objects, and fewer dependencies between Model and Views layers. This, in turns, gives us code that is easier to manage and test.

MVP in an Android App

Everything is easier to understand when you have examples. So here’s one on how to develop apps using MVP.

Used Libraries

It’s good practice to avoid reinventing the wheel. There are plenty of libraries that can make a developer’s life easier, so why not use them?

  • Dagger2 – for dependency injections,
  • Retrofit2 (beta2) – a HTTP client,
  • RxJava – we want to use the Reactive approach in our app architecture,
  • Butterknife – for binding fields and methods of Android views.

Implementation

Our room booking service is based on the Google Calendar application. Every room is actually a calendar. So, to make it more generic, we decided to use the term “Calendar” instead of “Room” in our code. In order to book a room, the user adds an event with a list of guests, a time slot, and a description.

As an example, we will use the scenario of displaying a list of available rooms. You can find the code for the entire app at Github RoomBookerMVP.

Model

Calendar model is pretty simple. It contains only 3 fields.

public class Calendar {
    public int id;
    public String description;
    public String summary;
    ...
}

Presenter

Every presenter class should implement following interface.

public interface Presenter<T extends View> {
    void onCreate();
    void onStart();
    void onStop();
    void onPause();
    void attachView(T view);
}

As you can see, it has methods that cover Activity/Fragments lifecycle methods. Even if from the MVP point of view Activities and Fragments are treated like Views, we have to keep in mind that they are base components of every screen. If we need to keep track of the activity lifecycle, we have to call a proper Presenter method that corresponds to the activity method.

The Presenter from our example is CalendarPresenter.

public class CalendarsPresenter implements Presenter<CalendarsView> {

    private Subscription getCalendarsSubscription;
    private CalendarsView calendarsView;
    private FetchCalendarsUsecase fetchCalendarsUsecase;

    public CalendarsPresenter(FetchCalendarsUsecase fetchCalendarsUsecase) {
        this.fetchCalendarsUsecase = fetchCalendarsUsecase;
    }

    @Override
    public void onStop() {
        if (getCalendarsSubscription != null && !getCalendarsSubscription.isUnsubscribed()) {
            getCalendarsSubscription.unsubscribe();
        }
    }

    @Override
    public void attachView(CalendarsView view) {
        this.calendarsView = view;
        getCalendars();
    }

    private void getCalendars() {
        calendarsView.showLoading();
        getCalendarsSubscription = fetchCalendarsUsecase.execute()
                .subscribeOn(Schedulers.io())
                .onErrorReturn(new Func1<Throwable, Calendars>() {
                    @Override
                    public Calendars call(Throwable throwable) {
                        throwable.printStackTrace();
                        calendarsView.showError();
                        return null;
                    }
                })
                .subscribe(new Action1<Calendars>() {
                    @Override
                    public void call(Calendars calendars) {
                        if (calendars != null) {
                            calendarsView.showCalendars(calendars.calendarList);
                        }
                    }
                });
    }
    ...
}

To obtain the list of calendars we use FetchCalendarsUsecase. The presenter doesn’t care how they’re obtained (local storage, rest service, etc.), it simply subscribes to Observable returned by the Usecase execute() method and, depending on the result, updates CalendarsView. We want to do this operation in the background, and pass the results to the UI thread. We have to eliminate an exception that can occur should the corresponding Activity be destroyed before the background task finishes. To do that, we keep an instance of an RxJava Subscription object (getCalendarSubscription) and unsubscribe it in the onStop() method called by Activity or Fragment that uses this Presenter.

View

In our application, every View interface extends an empty interface (no methods declared) called View. CalendarsView should be able to show some sort of progress bar as the calendars are fetched, display the calendar list, or an error message when fetching fails.

public interface CalendarsView extends View {
    void showCalendars(List<Calendar> calendars);
    void showLoading();
    void showError();
}

Next we create class that implements this interface.

public class RoomsActivity extends AppCompatActivity implements CalendarsView {

    @Inject
    CalendarsPresenter calendarsPresenter;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        initializePresenter();
    }

    public CalendarsComponent getCalendarsComponent() {
        return calendarsComponent;
    }

    @Override
    protected void onStop() {
        super.onStop();
        calendarsPresenter.onStop();
    }

    private void initializePresenter() {
        calendarsPresenter.attachView(this);
    }

    public void showLoading() {
        progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void showCalendars(final List<Calendar> calendars) {
        Handler mainHandler = new Handler(getMainLooper());

        Runnable myRunnable = new Runnable() {
            @Override
            public void run() {
                addRooms(calendars);
            }
        };
        mainHandler.post(myRunnable);
    }

    @Override
    public void showError() {

    }

    ...
}

In the Activity class, we have to call methods of the Presenter, like onStop(), or methods that responds to user action. Other than that, Activity should only contain the UI logic and respond to Presenter action.

Usecase

Every Usecase have execute() method that returns an Observable object.

public interface Usecase<T> {
    Observable<T> execute();
}

Usecase for fetching calendars is very simple, it uses a Repository (described later) object to fetch the list of calendars.

public class FetchCalendarsUsecase implements Usecase<Calendars> {

    private Repository repository;
    public FetchCalendarsUsecase(Repository repository) {
        this.repository = repository;
    }

    @Override
    public Observable<List<Calendar>> execute() {
        return repository.getCalendars().map(new ResponseMappingFunc<List<Calendar>>());
    }
}

Repository

Again, we start by defining the Repository interface in order to easily change repository implementation without modifying Presenter classes.

public interface Repository {

    Observable<ResponseWrapper<List<Calendar>>> getCalendars();

    ...
}

In our application we use Retrofit2 which, conveniently, supports RxJava, so our RetrofitRestRepository is not very complicated.

public class RetrofitRestRepository implements Repository {

    private ApiService apiService;

    public RetrofitRestRepository(Retrofit retrofit) {
        apiService = retrofit.create(ApiService.class);
    }

    @Override
    public Observable<ResponseWrapper<List<Calendar>>> getCalendars() {
        return apiService.getCalendars();
    }
    ...
}

Round-up

To sum up, let’s take a quick look at the diagram demonstrating how the above mentioned classes work together. When Presenter has to respond to some UI event, it calls the execute() method of the proper Usecase that returns Observable. All layers below Usecase also return Observables, that we chain together and subscribe to in Presenter. The Presenter reacts to whatever Observers emits and updates View.

Diagram 3

Basically, the method described above can be used to implement all sorts of new features. Now, if we want to completely change the look of this screen (but still display the list of Rooms), then we only have to change MainActivity, and implement showLoading, showError, and showCalendars methods. We don’t have to touch any other layer. The same goes for fetching calendars. If we’d like to add a database, then the only thing we have to do is modify FetchCalendarsUsecase.

Conclusion

The Model-View architecture is currently the most commonly used pattern in Android development. Implementing other types of architecture, like Model-View-Presenter, is not straightforward, because Activities and Fragments have a lot of responsibilities. But it’s worth the extra effort to do that even if it seems that the code is getting a bit too complex at the beginning and it burdens the developer with writing a lot of extra code. The truth is that as soon as you figure out how to use MVP (or any other architectural pattern, like MVVM) you will see that code is much easier to understand, scalable, and that its degree of complexity does not rise in step with the expansion of the application itself.

Resources

If you’re interested in developing Android applications using the Clean Architecture approach, you will find more on the subject under the following links:

Our Farewell to an Eventful 2015
Automate Testing & Build Delivery with fastlane and Travis CI