Tech

Clean architecture in Android with Kotlin, RxJava, Dagger 2

Updated on
June 16, 2023
Table of content
Show
HIDE

Even though we are living in the Agile world with a bunch of available Time and Energy management techniques that are easy to implement in your life, there are still a few things in software development that may increase your productivity and improve general quality of your code.

The thing I want to describe in this article is application architecture in Clean Architecture way. By implementing the Clean in your project you’ll have decoupled, testable parts for data storing, processing and presenting. Clean Application is extremely maintainable, because of interchangeable implementations it would be easy for you to change every aspect of your app: from UI and simple data processing to DB and API frameworks.

On the first sight you may consider Clean architecture as a bunch of abstract ideas without ‘real code’ power, but in this article I’ll try to show you how these may be implemented on the Android side.

At first let’s have a quick intro to the project structure: I will be using Views and Presenters to display the UI part of the application and Kotlin+RXJava2+Dagger2 to implement Clean Architecture principles in the work with networkdbcacheetc .

Kotlin

Kotlin features extremely simplify android development not just by allowing you to use smaller constructions of language, but maintaining semantically right functions in your app. This will allow you to think less about implementation details and more about the whole app structure. Functional part of Kotlin will reduce side effects of your code and make it easier to test and understand.

Looking for extra hands? Hire Dedicated Android Developers at Uptech. Contact us.

RXJava

RX operators that RXJava provides easy to use stuff for working with data streams and threads. But in my opinion, the main purpose of RxJava is to have universal structures for data flow in an application.
Seriously, you should check it out if you’re still not familiar with RxJava.

Dagger2

A fast dependency injector for Android and Java.
Dagger2 is a great library that allows you to @Inject everything you need where you need it and handle the lifecycle of created objects. Dagger2 is used to avoid detail-complicated boilerplate code of connecting architecture elements one to another.

Let’s get to the architecture

We will follow the dependency rule on this well-known picture to have an application with interchangeable parts.

an application with interchangeable parts

Just a few principles(S O L I D) will allow us to decouple data fetching and handling processes of our application, build interchangeable views and keep their interfaces clean and simple, with keeping solid abstraction level from frameworks and data layer implementations.

As I mentioned before, UI part of the application architecture will be built on Views and Presenters, you may find great guides to it here and here.
Data fetching and processing layers will be presented as UseCases and Repositories.

Sample

Starting from now we will examine a feature of changing the user’s email and what actions are performed on each layer.
If you’re not familiar with Clean Architecture approach, you should check Fernando Cejas’ article.

Data layer

Data layer is represented by Repository interfaces. Each repository works with a single, well-defined aspect of application (users, messages, events inbox, etc)

CODE:https://gist.github.com/TheMakhrov/55f850ad4b6e9bbd8a553054726ef924.js

The main purpose of this repository is to handle users workflow: API calls for creatinggettingpatching user entity.

Dagger module code:

CODE:https://gist.github.com/TheMakhrov/e76fbede9b5bc48fbda7a23556e58311.js

Notice please that returning value of @Provides annotated function is an interface, not the implementation. That approach will allow us to make huge changes to implementation leaving ‘client’ code without changes.

Domain layer

Domain layer’s element is UseCase. UseCase’s role is to combine data layer elements to perform a combination of data operations.

CODE:https://gist.github.com/TheMakhrov/e8a4b68c559bd92ec471c53c8ad25e42.js

Another important thing is different entities for each layer of application. As dependency inversion principle works in the other way works additional fields decreasing.
The purpose of different entities for each layer is to reduce the amount of information inner layer may depend on. E.g., you don’t need api version at your entity in domain and presentation layers of your application, you only need this property in data layers.

Presentation layer

Presentation layer consists of 2 elements: a passive View and a Presenter with presentation logic. View is completely passive, so it just calls presenter functions when any UI action is performed, and Presenter is deciding what actions must be performed on one or another UI event.

View:

CODE:https://gist.github.com/TheMakhrov/bea1625be77db1c562524260fba608cc.js

Presenter :

CODE:https://gist.github.com/TheMakhrov/a856c5c962053baade43492de37324b3.js

Calls stack

That was a quick dive into layers. Now let’s go through calls stack when user presses confirmButton.

Firstly, button click listener in view is called.

confirmButton.onClick {
presenter.onConfirmButtonClicked()
}

Then view calls the presenter’s method. In presenter we validate user input with ValidationUtils.isValidEmail(view.getEmailInput()) and show user validation error message if input is not valid. We setup observable to perform its emissions and notifications on a specified thread with observeOn() operator. If we don’t need to perform operations on the certain thread we’re letting UseCase or Repository to setup thread that observable will work on.

changeUserEmailUseCase.changeUserEmail(view.getEmailInput()) .observeOn(AndroidSchedulers.mainThread())

Finally, we subscribe to item emissions, specifying ‘happy’ and ‘error’ cases.

...

.Subscribe(
{ //Email Is Successfully Changed },
{ //Error While Changing Email }
)

UseCase itself will use implementation of repository to perform email changing operation.

AppUserRepository.ChangeUserEmail(Email)
.Map { DomainMapper.FromDataUserToDomain(It) }

It also maps elements to the understandable by UI layer type, by reducing amount of outer layer dependencies or adding fields that might be needed to display content .

The repository will perform email changing operation with DB, Firebase or API, cache results in local memory and distribute new info to subscribers through RxJava subjects.

Conclusion

You still may spend your time decoupling feature parts from your code to create some sort of code reusability in your project, or you can use this way of organizing things in Android, which allows you to create a testable and maintainable solution for your clients and customers. And build the app that will take changes easily, because its parts are feature-based and guaranteed, you wouldn’t rewrite half of your app for a minor changes, hating your client by the way