When first building Android apps, many developers might start by relying on Model View Controller (MVC) patterns and usually end up writing most of the core business logic in activities or fragments. The challenge is that writing tests that can validate the app's behavior is difficult to do because the code is often so closely tied to the Android framework and the various lifecycle events. While automated UI testing could be written to validate individual activities or fragments, maintaining and running them over the long-term is often difficult to sustain.
There are currently three major approaches to architecting your Android apps:
Standard Android (Model-View-Controller) - This is the "default" approach approach with layout files, Activities/Fragments acting as the controller and Models used for data and persistence. With this approach, Activities are in charge of processing the data and updating the views. Activities act like a controller in MVC but with some extra responsibilities that should be part of the view. The problem with this standard architecture is that Activities and Fragments can become quite large and very difficult to tests. You can learn more on this blog post.
Clean Architecture (Model View Presenter) - When using MVP, Activities and Fragments become part of the view layer and they delegate most of the work to presenter objects. Each Activity has a matching presenter that handles all access to the model. The presenters also notify the Activities when the data is ready to display. You can learn more in the sections below.
Data-binding MVVM (Model-View-ViewModel) - ViewModels retrieve data from the model when requested from the view via the Android data binding framework. With this pattern, Activities and Fragments become very lightweight. Moreover, writing unit tests becomes easier because the ViewModels are decoupled from the view. You can learn more on this blog post.
Clean architecture principles, as espoused by Robert Martin (also known as "Uncle Bob"), attempt to focus the developer on thinking through the app's core functionality. It does so by separating the architecture of app into three major layers: how the app shows the data to the user (presentation layer), what are the core functions of the app (domain or use case layer), and how the data can be accessed (data layer). The presentation layer sits as the outermost layer, the domain layer sits in the middle layer, and the data layer resides in the inner layer.
It's important to note that each layer has its own data model, and data can only be exchanged between layers and usually flows only in one direction (i.e. outer to inner, or inner to outer) Anytime data needs to be exchanged, usually a converter is used to map one layer's model to another. In this way, a boundary of separation is created that helps limit changes in one layer to cause unintended side effects in other layers.
At the data layer, every source (i.e. file, network, memory) of data should be represented using the repository data pattern. There are many different ways of implementing this repository pattern, but the ultimate goal is to expose an interface that defines the different queries that need to be performed in order to abstract away the type of data source used. The underlying implementations can therefore be swapped regardless of the type of data source used.
Clean architecture introduces more abstractions and attempts to apply single responsibility principles in Android development. While there may be concerns about this approach adding more complexity, slow performance, and poor testability, it has been shown to work successfully in production apps (see this Droidcon talk or this Droidcon 2016 talk).
In Android app development, the traditional "Model / View / Controller pattern" is often being dropped in preference for the "Model / View / Presenter" pattern. The Model-View-Presenter (MVP) architecture comprises:
What we want to achieve by using MVP are simpler tasks, smaller objects, and fewer dependencies between Model and Views layers. This, in turns makes our code easier to manage and test. Major differences from MVC include:
The easiest method for understanding this is multiple specific examples with sample code. Check out these useful resources for an in-depth look:
If you're attempting to migrate towards clean architecture without necessarily rewriting your entire code base, your best approach is to first try to isolate and encapsulate your logic by moving it outside of your Activities or Fragments. Moving towards the Model-View-Presenter (MVP), which is a popular way of structuring your presentation layer, should probably be your first step. There is no exact way of implementing this approach, so here are a few recommended links:
The following template projects are built to act as a starting point for this architecture:
Other tutorial articles: