A Dependency Injection Showdown
By: Mark Ng
Tags: Kotlin, Android, Android App Development, AndroidDev, Dependency Injection

Choosing a dependency framework for your Android app is a big decision, it’s not something that you can easily replace later on. Dependency injection spans all layers of your app so it’s important that the DI framework that you choose meets all your requirements and that includes testing.

I am going to compare 4 popular frameworks: Dagger, Koin, Kodein, and Toothpick. It’s interesting that all of these frameworks also run on the JVM and 2 of the frameworks also integrate with the popular Ktor framework. I am going to demonstrate how to integrate these DI frameworks into a simple application. Although the application will run on Android many of these concepts can be easily applied to non-Android projects.

The application contains a GreetingService which may either return “Good morning” or “Good evening” depending on the hour of the day.

The TimeService is used to return the current hour of the day. The GreetingService will use the TimeService to determine if it is morning or night and tailor the message accordingly.

The MessageData is used in the greeting message to add additional text to the message.

As you can see it’s a pretty simple example. I’ll show you how to use each DI framework and more importantly how to test your code. Even if your a non-Android developer you should still be able to understand these examples. Basically, we initialize our DI container when our application starts up using the Application class and we display our greeting message on the screen in the Activity class. That’s pretty much all the Android knowledge you need.

Framework Comparison

Before we get started here is a high-level overview of each framework showing the popularity, who supports its and where it runs. Later we’ll dive deeper into how each framework is used and see what the code looks like. As you can see Dagger is by far the most popular framework with 14.8K stars on Github.

Now, let's get started!

Koin

Koin was designed to be a pragmatic lightweight dependency injection framework that doesn’t use code generation, proxies or reflection. Over the last couple of years it has become very popular due to its simplicity.

Koin code

Koin uses the Module to wire up its dependencies.

Here the DI container is initialized with the Module declared above.

To display the greeting on the screen we need to inject the GreetingService and call the service after it is injected.

Koin testing

Koin provides its own mock classes that making testing the GreetingService really easy so you don’t need another mocking framework.

Performance metrics from the Koin website.

Overall I found it really easy to setup Koin, it has a very nice DSL, good support for Android and I had no trouble with testing the injection.

Good

Good documentation

Easy mocking for testing

Simple & clean DSL

Supports scopes

Android & JVM support

2nd most popular framework in Android

Ktor support

Bad

Kotlin Multi-Platform support (Update Oct 2020 : Coming soon in alpha)

get() in module

Slower than the DI frameworks that generate code

Doesn’t support JSR 330

Kodein

The Kodein framework was designed to provide painless dependency injection in Kotlin projects. It’s one of the first pure Kotlin dependency libraries and supports multiple platforms including iOS, Javascript, Android, and the JVM.

Kodein code

Wiring up the dependencies is pretty similar to Koin. In the Module you bind the interfaces to the implementations.

The DI container is also initialized with the Module and started in the Application . Kodein uses an interface called KodeinAware to mark the classes that are used for dependency injection.

The Activity also needs to use the KodeinAware interface so the GreetingService can be injected.

Kodein testing

Kodein provides a nice dependency override feature so you can easily inject fakes into the GreetingService.

It took me a little bit longer to get the Kodein example up and running mainly due to the documentation of the website being out of date. The DSL is similar to Koin but not as clean. I also found it to be a little bit more complicated than Koin.

Good

Supports Kotlin Multiplatform

Easy to test with fakes

Ktor support

Good Android support

Simpler than Dagger

Bad

Documentation not up to date

instance() in module

DSL and syntax not as clean as Koin & Toothpick

More complex than Koin & Toothpick

Slowest of all 4 frameworks

JSR 330 support

Dagger

Dagger is a fully optimized dependency framework for Android using generated code to avoid reflection. It is maintained by Google and is their recommended solution for doing DI on Android.

Dagger code

A Dagger Module is a little bit more verbose than the Modules used in the other frameworks. It uses annotations to describe the classes and wire up the dependencies as follows;

The constructor of the GreetingServiceImpl also needs to modified to add the @Inject annotation for Dagger to declare it as a Singleton. This also needs to be done for the TimeServiceImpl. Note that you use also need to remove the default message for the MessageData class otherwise will complain about multiple constructors.

Dagger requires an additional Component class to specify the Modules that will be used in your app. You also need to define the methods that will inject your classes and a Factory to inject the String into the constructor of the MessageData class.

The generated class DaggerComponent is used to start the DI container and is initialized with the Module .

To display the greeting on the screen you need to retrieve the Dagger Component from the Application and inject the Activity with the GreetingService .

Dagger testing

I checked the Dagger codelab and the recommendation way to test your classes was with mocks without using Dagger. That’s one of the reasons why Google recommends constructor injection over field injection.

Dagger still requires quite a lot of boilerplate to wire up your dependencies and use it in your app. It will be a welcome relief when Google addresses these issues and simplifies the syntax.(Update Aug 20 : Hilt has been released to address these issues however it’s still in alpha) If you work as a consultant and work on many different apps most likely you will run into Dagger eventually, so it’s one of those frameworks that you have to know anyway.

Good

JSR 330 support

Feature-rich

Good documentation & codelab

Lots of examples and blog articles

Most popular DI on Android

Google support

Compile-time checking of dependencies

Fast

Google working to addressing the bad

Uses incremental annotation processing

Bad

Overly complex

Need to reference generated classes

Boilerplate

Can be slow working with your IDE

No DSL

Kotlin Multi-Platform support

Toothpick

Toothpick is a scope tree based Dependency Injection (DI) library for Java/Kotlin/Android and maintained by Groupon. It is a full-featured, runtime based, but reflection-free, implementation of JSR 330.

Toothpick code

Toothpick’s DSL is the easiest to read, it also generates factories for instance creation avoiding the need for the ugly method calls such as get() or instance().

For Toothpick to generate these factories you need to add the @InjectConstructor annotation to the class being injected. Alternatively, you can also use the @Inject annotation constructor syntax which was used in the Dagger example.

The Module is installed in the root scope when the application starts up.

The GreetingService is injected into the Activity using the root scope. Once injected the GreetingService can be used to display the greeting on the screen.

Toothpick testing

The ToothPickRule class allows you to inject the class that you are testing with mocks.

You can also override classes with fakes by installing a test Module .

Just for completeness, this how you would use Toothpick and Robolectric.

Performance metrics from the Toothpick website.

On an average of 50 runs, the cost of setting up the injection system, i.e creating an injector (RG), a component (Daggers), and a scope (Toothpick) are:

RoboGuice 4: 105 ms

Dagger 1: 20 ms

Dagger 2: 22 ms

Toothpick: 1 ms

Toothpick’s clean DSL and clear use of scopes makes it easy to understand and use Toothpick on your project. It’s the fastest DI framework around and one of the easiest to test. Although it uses generated classes you don’t have to reference any of these classes.

Good

JSR 330 support

Supports both annotations & delegates

Very good documentation

Supported by Groupon

Cleanest DSL compared to Koin & Kodein

Easy to test

Fastest of the 4 frameworks

Uses incremental annotation processing

Bad

Kotlin Multi-Platform support

Not many examples & blogs posts about it

Final standings

When I evaluate frameworks usability and testability are always at the top of my list. A framework doesn’t have to be the fastest or be written entirely in Kotlin, as long as the framework provides nice Kotlin extensions and runs at a reasonable speed that is good enough for me. Knowing that the framework is well supported and maintained is also another consideration. Having good documentation is also another must and that’s why some frameworks such as Spring have dominated their domains.

Taking this all into consideration, the winner of this shoot out is Toothpick followed closely by Koin. I feel it would be quite easy for the contributors of Kodein to address the issues I had and also update their documentation. I put Dagger last because in its current form it’s just too complex and verbose to use. It’s good to know that Google is committed to addressing these issues and improving the developer experience. Hopefully in the future it can improve its standing and bring bigger guns to the next shoot out!

1st Toothphick — The winner of this shootout.

2nd Koin — A close second.

3rd Kodein — A decent contender

4th Dagger — Not the fastest shooter out there, it needs fewer bullets not more.

The working code for all the examples including the tests can be found at this repo.

Kotlin Multiplatform & DI

I am really excited about KMP (Kotlin Multiplatform) and hope it lives up to its promises, although it’s still experimental many companies are now using it in production. I have been wondering for a while about how or if DI will be used in a KMP app. I know that many iOS apps don’t use DI and that's why I am wondering if the shared code would still it. At present only Kodein supports KMP and according to Kevin Galligan there is also a forked version of Koin that will support KMP in the future. My prediction is that KMP will disrupt the way we write apps and also the libraries we use, we just have to wait a little bit longer for that to happen.

Thanks to Karl Maul for editing.

Btw, we use both Toothpick and Dagger in our production apps. If you liked this article about dependency injection also check this one out.

If your into KMP check this one out.

https://medium.com/swlh/the-first-step-on-your-kmp-journey-changing-your-microservices-4de3f99f6e56