As NaN’s mobile team, we are committed to continuous improvement. That includes looking for and trying the latest technologies regularly. In that context, we decided to look for the optimal way to manage dependency injections when working with the Android Architecture Components (LiveData, ViewModel and Room) and Kotlin.
We started a new pet project that we used as our playground to test different libraries. And committed to respect programming best practices and use TDD strictly throughout the whole development process.
This led us to question the use of Dagger 2. It did not provide the clarity we were looking for to manage dependency injection. Dagger 2 is both stable and powerful. The only limitation we found, is that both the library and its documentation are complex to apprehend. In particular, for each project, a lot of boilerplates are necessary to make it work. As a result, when using Dagger 2 there is a temptation to copy and paste in order to avoid getting back to spending time on studying the documentation.
The risk of copy pasting is that, even though it works, there is no deep understanding of the issue so it is code you can’t fully rely on. In order to keep the code clean we need to understand the abstraction and how the library is built.
So we decided to look into other options. We had a pragmatic approach: we tried all options directly into our project and compared the libraries based on the ease of use and apprehension that we felt they provided.
In our case, Koin was the best option. It is a recent open source library based on functional programming and DSL. There is already some info available on the Koin website. It is not complete yet, but work is in progress and it’s moving fast! The creators welcome all contributions and are available to provide information about how Koin works internally.
We believe Koin is really worth considering as an option for dependency injection management. We used it in the Android context. It could also be used in other environments.
Dagger 2 vs Koin
The simplicity of the koin implementation
In order to properly compare the two implementation options, we will focus on two similar projects. Both projects are basically managing one single entity into the database. One is about food and the other is about pets. Both are using Kotlin and the android architecture components.
So, what is going on here? We are applying dependency injection, just that. In two different ways.
How to implement the dependency injection
Dependency Injection with Dagger 2
Our objective here is not to explain the Dagger 2 approach, but rather to show its differences with Koin. In order to do so, we will share the repository to the source code of a pet project we developed to give users the possibility to share info about their everyday diet with their nutritionist. It is a native Android application developed using Dagger 2.
If you’re looking for a quality tutorial on how to use Dagger 2, we recommend you take a look at Antonio Leiva’s post: How to use Dagger 2 on Android with Kotlin (KAD 20).
Dependency Injection with Koin
As we mentioned, Koin offers an efficient and clean way to manage dependency injection. Here you’ll find a step by step guide on how to use it.
You can also take a look at the implementation of one of our pet project that is about… pets! We developed an MVP for an Android app that easily puts in touch a lost pet and its owner. Here is the link to the repository of Rescatadores.
This same project inspired another post about A clean approach to deferred authentication in Android.
Koin Modules: the components to be injected. In our case there are:
- The database through Room
- The repository
- The viewmodel for MVVM implementation
- The factory of pets (this necessity came from TDD)
- The adapter for the recyclerview.
Then we inject the components into the activity:
To get started with the configuration, we need to extend the Application. We use the startKoin method to register the components detailed in the modules. In our case, just the PetModule.
Finally, it’s important to go to the AndroidManifest.xml and explicitly set that our MainApplication class will be used. This can be done by adding the android:name line.
It is not a big deal to implement, but it’s common to miss it and get stuck because it was overlooked.
Koin core concepts
We are going to point to specific parts of this implementation and detail them. It is also well explained on the official Webpage that we mentioned earlier.
startKoin: it is the starting point. This method receives a list of “modules”
modules: they contain the “provide definitions” declared under the applicationContext function
provide: each “provide definition” represents a component to be injected. Into the lambda of the “provide definition”, we register a function with the capability to create that component.
get(): resolves a component dependency. When you are trying to provide a component that needs another component (which will be injected), the get() solves it. Conceptually, it looks for and finds a provider for that type and uses it.
By: this came from kotlin. It’s a “delegated property”. We can find an explanation in the official documentation here.
Do you want to migrate?
We are really enthusiastic about this library and see a lot of potential in it. If you are interested in migrating your project to Koin, we recommend the following post, written by the creator himself: Moving from Dagger to Koin — Simplify your Android development