Skip to content

An Android template application showcasing: Multi modular clean architecture, reactive patterns, dependency injection, integration with jetpack libraries, testing and CI/CD.

Notifications You must be signed in to change notification settings

wednesday-solutions/android-template

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Android Template

An Android template application showcasing: Multi modular clean architecture, reactive patterns, dependency injection, integration with jetpack libraries, testing and CI/CD.

Expert teams of digital product strategists, developers, and designers.


We’re always looking for people who value their work, so come and join us. We are hiring!



Getting Started

  • Open the project in Android Studio and sync dependencies with gradle.
  • Run the app by pressing the run button in android studio or by pressing control + R.
  • Go through and setup the scripts in the scripts directory.

App Secrets

Sensitive information such as the api keys is managed via local.properties file. This file in not checked into version control to keep the sensitive information safe. If you want to run the project locally you need to add your own api keys. Look at the local.skeleton.properties file for all the keys you need to include in your local.properties file.

You can get the Open Weather API key from openweathermap.org.

Architecture

The architecture of the template facilitates separation of concerns and avoids tight coupling between it's various layers. The goal is to have the ability to make changes to individual layers without affecting the entire app. This architecture is an adaptation of concepts from The Clean Architecture.

Layers

The architecture is separated into the following layers

  • presentation: All UI and state management elements like Activities / Fragments (soon composables), View Models, Components, etc.
  • navigation: navigators to navigate between Screens.
  • interactor: provides feature specific functionality and handle coroutine contexts.
  • domain: use cases for individual pieces of work.
  • repository: repositories to manage various data sources.
  • service: services provide access to external elements such as databases, apis, etc.

Module Structure

Each layer has 3 modules:

  • A module for the interfaces. These modules have no suffix. For example: service
  • A module for the implementation. These modules are suffixed with -impl. For example: service-impl
  • A module for Dependency Injection. These modules are suffixed with -di. For example: service-di

Entity Modules

The layers presentation, domain and services each have an entity module as well.

  • presentation-entity: Data Classes that model the visual elements used by the widgets.
  • domain-entity: Data classes for performing business logic manipulations. They act as a abstraction to hide the local and remote data models.
  • service-entity: Contains local models (data classes for the database) and remote models (data classes for the api).

Entity Naming Convention

  • Presentation entities are prefixed with UI (eg: UICity).
  • Domain entities do not have any prefix. (eg: City).
  • Service entities are of 2 types:
    • Local / Database entities are prefixed with Local (eg: LocalCity).
    • Remote / API entities are prefixed with Remote (eg: RemoteCity).

Inter-module Dependencies

There is a pattern in which all these modules depend on each other.

  • The interface module in a layer is just the name of the layer.

Example: repo-interface is just repo

  • The implementation module in a layer is just the name of the layer, suffixed by -impl.

Example: repo-implementation is just repo-impl

  • The implementation modules depend on the interface modules of the same layer and the layer directly below it.

Example: repo-impl depends on repo and service.

  • The interface modules may depend on the interface modules of the layer below.

Example: repo depends on service.

  • The di modules depend on the interface and implementation modules of the same layer. And may also depend on the interface module of the layer below.

Example: repo-di depends on repo, repo-impl and service.

Apart from these, the layer that have entity modules depend on entity module of the same layer. The layers that don't have entity modules depend on the entity modules of the layer above and below.

Gradle setup

  • All the dependencies are listed in the buildSrc module in the Dependencies.kt file..
  • Android related setup like build types and flavors is in android.gradle which is imported in each module.
  • Linting setup is done in lint.gradle which is imported in each module.
  • The build.gradle.kts files for each module are renamed to module-name.gradle.kts so that it is easy to locate them. For example, the gradle file for service module is called service.gradle.kts.

Understanding the Presentation Layer

The presentation layer houses all the visual components and state management logic.

The base directory has all the reusable and common elements used as building blocks for the UI like common components, base classes, extensions, etc.

View Model

Each View Model is a sub class of the BaseViewModel. The BaseViewModel. View Models also have the SavedStateHandle injected into them.

View Model exposes a LiveData of ScreenState from the SavedStateHandle. Along with the ScreenState it also exposes a LiveData of Effect.

Implementations of the BaseViewModel can also choose to handle Intents.

Screen State

ScreenState encapsulates all the state required by a Fragment. State is any data that represents the current situation of a Page.

For example, the HomeScreenState holds the state required by the HomeFragment.

Effect

Effects are events that take place on a fragment that are not part of the state of the screen. These usually deal with UI elements that are not part of the xml layout.

Showing a snackbar or hiding the keyboard are examples of an effect.

Intent

Intent is any action takes place on fragment. It may or may not be user initiated.

SearchScreenIntent has the actions that can happen on the SearchFragment.

Components

Components are reusable parts of UI. Components can be used in any fragment using the by composable delegate provided by the BaseFragment.

Components can be anything from a simple component to hide and show the loading indicator to a complex component like ListComponent that can manage normal and nested recycler view efficiently.

Fragment

Each Fragment must extend the BaseFragment. The BaseFragment provides the ViewModel with the navigator and the Screen. It listens to the screen state and effect live data from the view model and notifies the fragment about it. It also binds and unbinds all the components in the appropriate lifecycle callbacks.

Each Fragment may receive the Screen as arguments when navigating.

Screen

A Screen is a class that represents a Fragment in the context of navigation. It holds the path or id used by the navigator to navigate to a Fragment and also holds any arguments required to navigate to that Fragment.

Flavors

The template comes with built-in support for 3 flavors

  • Dev - This flavour is used across the development stage
  • QA - This flavour would be used when the app is under review by the QA Team or the Project Manager
  • Prod - This flavour would be used when the app is ready to be deployed to a store or shipped to the user

Note: Flavour specific configurations can be made in the app.gradle file under the buildTypes block

Content

The Android Template contains:

Continuous Integration and Deployment

The Android template comes with built in support for CI/CD using Github Actions.

CI

The CI workflow performs the following checks on every pull request:

  • Lints the code with ./gradlew lintRelease. (As an additional process, we also run ./gradlew ktlint which is based on the Ktlint Kotlin linter)
  • Runs tests using ./gradlew testDebugUnitTest.
  • Build the android app as the dev flavor to check if the building process works.

CD

The CD workflow performs the following actions:

  • Bump the versionCode by 1. For details read Version your app
  • Build a release apk (prod flavor).
  • Sign the apk using apksigner
  • Upload apk to app center.
  • Upload apk as artifact to release tag.
  • Commit the updated version to git.

Note: It is recommended to keep your keystore and its essentials like: alias, password safe and encrypted, inside your Github Secrets

Android CD setup

For the android CD workflow to run, we need to perform the following setup steps:

  • Follow these instructions to generate an upload keystore. Note down the store password, key alias and key password. You will need these in later steps.
  • Use openssl to convert the jks file to Base64.
openssl base64 < android_template_keystore.jks | tr -d '\n' | tee android_template_keystore_encoded.txt
  • Store the base64 output on Github Secrets with the key name KEYSTORE.
  • Save the store password in github secrets with key name KEYSTORE_PASSWORD.
  • Save the key alias in github secrets with key name KEY_ALIAS.
  • Save the key password in github secrets with key name KEY_PASSWORD.
  • Create a distribution on app center and get the upload key. You can get it from from Settings.
  • Save the app center upload key on github secrets with key name APP_CENTER_USER_API_TOKEN.
  • Save the group name as GROUP_NAME inside github secrets. Example: Testers
  • Save the organisation name as ORG_NAME inside github secrets. Example: Wednesday Solutions
  • Save the app name as APP_NAME inside github secrets. Example: Android Template

Caution: Respect the of the value inside secrets or else AppCenter APIs might have problems looking for your app