Android Architecture

This tutorial contains notes about architecture for Android applications which improves testability.

1. Architectures for Android

The Android default templates encourages the creation of large activities or fragments. These components typically contain both business and UI logic. This makes testing and therefore the maintenance of Android applications harder.

Several patterns are popular within the Android community to improve testability.

The most popular architecture choices are:

  • Model View Presenter (MVP)

  • Model View View Model (MVVM) together with Android Data Binding

The view in MVP or MVVM is not the same as the View class in Android. A view in MVP it usually implemented via a fragment, activity or a dialog.

2. The Model View Presenter architecture for Android

The Model View Presenter (MVP) architecture pattern improve the application architecture to increase testability. The MVP pattern separates the data model, from a view through a presenter.

mvp overview

The following demonstrates an example data flow throw the MVP.

Example data flow in MVP

2.1. The view

A view component in MVP contains a visual part of the application.

It contains only the UI and it does not contain any logic or knowledge of the data displayed. In typical implementations the view components in MVP exports an interface that is used by the Presenter. The presenter uses these interface methods to manipulate the view. Example method names would be: showProgressBar, updateData.

2.2. The presenter

The presenter triggers the business logic and tells the view when to update. It therefore interacts with the model and fetches and transforms data from the model to update the view. The presenter should not have, if possible, a dependency to the Android SDK.

2.3. The model

Contains a data provider and the code to fetch and update the data. This part of MVP updates the database or communicate with a webserver.

2.4. Considerations for using the MVP design pattern

MVP makes it easier to test your presenter logic and to replace dependencies. But using MVP also comes with a costs, it makes your application code longer. Also as the standard Android templates at the moment do not use this approach, not every Android developer will find this code structure easy to understand.

2.5. Comparison to Model View Controller

In the Model View Presenter pattern, the views more separated from the model. The presenter communicates between model and view. This makes it easier to create unit tests Generally there is a one to one mapping between view and Presenter, but it is also possible to use multiple presenters for complex views.

In the Model View Controller pattern the controllers are behavior based and can share multiple views. View can communicate directly with the model.

MVP is currently on of the patterns that the Android community prefers.

3. The Model View View Model architecture for Android

The Model View View Model design pattern is also known as Model View Binder.

MMVM pattern

3.1. The view

A view component in MVP contains a visual part of the application.

The view binds to observable variables and actions exposed by the view model typically using the data binding framework.

The view is responsible for handling for example:

  • Menus

  • Permissions

  • Event listeners

  • Showing dialogs, Toasts, Snackbars

  • Working with Android View and Widget

  • Starting activities

  • All functionality which is related to the Android Context

3.2. The view model

The view model contains the data required for the view. It is an abstraction of the view and exposes public properties and commands. It uses observable data to notify the view about changes. It also allows to pass events to the model. It is also a value converter from the raw model data to presentation-friendly properties)

The view model has the following responsibilities:

  • Exposing data

  • Exposing state (progress, offline, empty, error, etc)

  • Handling visibility

  • Input validation

  • Executing calls to the model

  • Executing methods in the view

The view model should only know about the application context. the application context can:

  • Start a service

  • Bind to a service

  • Send a broadcast

  • Register a broadcast receiver

  • Load resource values

It cannot:

  • Show a dialog

  • Start an activity

  • Inflate a layout

3.3. The model

Contains a data provider and the code to fetch and update the data. The data can be retrieved from different sources, for example:

  • REST API

  • Realm db

  • SQLite db

  • Handles broadcast

  • Shared Preferences

  • Firebase

  • etc.

Basically the same as the model in the MVP.

3.4. Differences to MVP

MVVM uses data binding and is therefore a more event driven architecture. MVP typically has a one to one mapping between the presenter and the view, while MVVM can map many views to one view model In MVVM the view model has no reference to the view, while in MVP the view knows the presenter.

4. Using build flavors as architectural style to improve testability

One way to improve testability in your application is to use build flavors. In your build flavor you define different classes for providing data.

For example, if you have two flavors 'prod' and 'mock' you could have the following different implementations in your flavors.

This could be a theoretical implementation for the 'mock' flavor:

public class Injection {

    public static ImageFile provideImageFile() {
        return new FakeImageFileImpl();
    }

    public static NotesRepository provideNotesRepository() {
        return NoteRepositories.getInMemoryRepoInstance(new FakeNotesServiceApiImpl());
    }
}

This could be a theoretical implementation for the 'prod' flavor:

public class Injection {

    public static ImageFile provideImageFile() {
        return new ImageFileImpl();
    }

    public static NotesRepository provideNotesRepository() {
        return NoteRepositories.getInMemoryRepoInstance(new NotesServiceApiImpl());
    }
}

If you application code you can access the provided implementation via Injection.provideNotesRepository(). Depending which flavor you are build you would receive the mocked version or the real version. Your unit test would build again the mock flavor to mock away the external dependencies.

5. Using dependency injection as architectural style to improve testability

Dependency injection is another way of building testable applications. Android applications are using in most cases Dagger 2 for dependency injection. In case the tests are running mock or fake objects are injected. If the real application is started the correct objects are injected.

6. Writing tests for the presenter

Typically the presenter has dependencies, for example to the implementation of the view contract. You mock or fake these dependencies away to unit test the presenter.

For example, to test the presenter we do not need to know if the view displays a dialog or progress indicator. But we want to test that presenter calls a method on the view to display the progress information.

7. Exercise Model View Presenter

Here you will design a small application for maintaining tasks. === Create a new application.

Table 1. New Android project
Property Value

Application Name

Task Manager

Package name

com.vogella.android.mvp.tasks

Minimum SDK

Latest Android release

Template

Empty Activity

Activity

MainActivity

Layout

activity_main

Backwards Compatibility (AppCompat)

true (selected)

Currently you only designing the application, e.g., the data download will be implemented later.

7.1. Create packages

The project groups classes according to their use case. Create the following Java packages:

  • createtask

  • data

  • overview

  • util

7.2. Create TasksActivity

Create a new TasksActivity and make it your main entry point for the application.

You have an overview screen with the repositories of the selected user. If you click on the repository you would see details for this repository.

The contract for this view and presenter should be defined via the GitHubContract interface. Whenever the view needs to perform an operation on the presenter, it would call one of its methods. If the presenter finishes, it would call back the view, via the defined interface methods.

import android.support.annotation.NonNull;

import java.util.List;

/**
 * This interface specifies the contract between the view and the presenter.
 */
public interface GitHubContract {

    interface View {

        void setProgressIndicator(boolean active);

        void showRepositories(List<String> repositories);

        void showAddRepository();

        void showRepositoryDetails(String repositoryId);
    }

    interface UserActionsListener {

        void loadRepositories(boolean forceUpdate);

        void addRepository();

        void openRepositoryDetails(String repositoryId);
    }
}

In the create method of the activity you would instantiate the responsible presenter and pass a reference to the presenter to the view.

 @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // more code
        presenter = new GithubPresenter(this); // potentially more parameter
    }

Implement at least a button in your activity for the refresh action. Call loadRepositories on your presenter in the onClick method of the button. In the presenter call the setProgressIndicator method on the view, create some test data and call showRepositories repositories on the view with this data.

Would you be able to write unit tests for the presenter which could run on the JVM?

8. Android Architecture resources