As Android evolves, developers become more focused on creating applications with good architecture. Good architecture helps reduce development time, makes the code easy to understand, maintains code effectively, and makes it testable. In this article, we will discuss the recommended architecture for Android development, known as Clean Architecture, and some good practices for building Android apps.
The Clear Architecture includes three layers: the UI Layer, Domain Layer (optional), and Data Layer.
The UI Layer, or presentation Layer, is responsible for interacting with the UI and includes Activity, Fragment, and ViewModel. These components communicate with the domain and data layers to retrieve data and update the UI.
The Domain Layer is an optional layer that sits between the UI and Data Layers. It is responsible for encapsulating complex business logic or simple business logic that is reused by multiple View Models. Also, this layer is independent of any other layers which means that changes in other layers will have no effect. The components of the Domain Layer include Models, which define the core structure of the data used within the application, and Repositories, which are interfaces used by the use cases and implemented in the Data Layer.
The Data Layer contains the business logic of the app and the implementations of repositories declared in the Domain Layer. The components of the Data Layer include Models, which includes Dto and Entity models, Mappers, Network, Cache and Data Sources. The Dto model defines POJO of network responses, while the Entity model defines the schema of the database. Regarding the Mappers, it transforms Data between domain, dto and entity models. The network handles the operations. The cache is responsible for performing caching operations. And Data sources is responsible for deciding which source (network or cache) will be used when fetching and presenting the data to View Models.
“Programming is a creative field, and building Android apps isn't an exception. There are many ways to solve a problem; you might communicate data between multiple activities or fragments, retrieve remote data and persist it locally for offline mode, or handle any number of other common scenarios that nontrivial apps encounter. In most cases following them makes your code base more robust, testable, and maintainable”
Avoid designating your app's entry points, such as activities, services, and broadcast receivers, as sources of data. Instead, they should only coordinate with other components to retrieve the subset of data that is relevant to that entry point.
Do not define multiple unrelated responsibilities, such as data caching and data binding, in the same class.
Don't expose an internal implementation detail from a module to gain a bit of time on the development.
Avoid writing the same boilerplate code again and again. Use Jetpack libraries and other recommended libraries to handle the repetitive boilerplate.
Don't mix the logic of Network and Cache modules in one place. If modules are well-isolated, it’s easier to test each part effectively.
Persisting data allows users to use the app’s functionality even when the device is in offline mode.
Modularization is the process of organizing a code base into loosely coupled and self-contained parts. Each part is a module, and each module is independent and has a clear purpose. There are many advantages to modularization, including:
Common patterns in modularization include: