Choosing a modern dependency injection framework for Android

Rebecca Blum
3 min readNov 22, 2021

--

At Metromile, our Android app uses dependency injection to control access to and scope of objects throughout our code. We had been using Dagger 1 and were ready to migrate to one of the newer frameworks. We explored the benefits and limitations of two popular libraries: Koin* and Dagger Hilt. Both Koin and Hilt provide several advantages over home-grown or legacy dependency injection options:

  • It’s easy to create and change the definitions for how your classes are created and shared across your codebase. A few annotations and you can start using Hilt; a few lines of code in a module and you’re ready to go with Koin.
  • Dramatically fewer lines of written code from Dagger 1 or even 2.
  • Robust support for unit testing with mocks or fakes.
  • Off-the-shelf integration with Jetpack ViewModels.
  • Active user communities and excellent documentation.

The following post assumes some familiarity with dependency injection frameworks. Here are some additional resources if you’re getting started:

And I would be remiss not to mention Ankit Raj’s excellent Koin vs. Hilt writeup with more explanation of how each of them works under the hood.

*Yes, Koin is technically a service locator, not a dependency injector.

Putting it into practice: Koin vs. Hilt cheat sheet

Here is a quick reference for how Koin and Hilt differ in practice in a typical MVVM Android application. They’re both stable and widely used options.

Embedded Airtable for side-by-side comparison of Koin and Hilt. It’s hard to put tables in a Medium post.

Reflections on our decision

After an in-depth review of both frameworks and some sample implementations, we chose Hilt for several key reasons:

  • Since we were already using Dagger 1, many of our code patterns and annotations could remain the same
  • We preferred to stick to compile-time dependency resolution to reduce the possibility of runtime crashes
  • Hilt is officially prescribed by Google for DI and thus is likely to remain well-documented and maintained for a while (“a while” being entirely relative in mobile development, of course).
  • Though this wasn’t part of our research at the time of the decision, Hilt also provides some integrations with Jetpack Compose

I was the team member who planned and executed the migration from Dagger 1 to Hilt, and on reflection, my feelings are mixed. The developer experience during the migration was slow and frustrating. There was a high cost of switching branches and rebuilding, and I spent many cumulative hours trying to stay productive while the code compiled. There wasn’t good documentation for converting our unit tests from Dagger 1 directly to Hilt, and test injection doesn’t behave quite the same way. Finally, since Hilt requires the host application to be annotated, there was no opportunity for incremental migration (e.g. one module at a time, which would have been possible if switching to Koin).

When I’m writing code, I prefer Koin’s slick DSL and quick startup time, even though it requires more care and testing to ensure your dependencies are appropriately resolved. But I agree that Hilt was the more reliable choice for Metromile, and we were able to preserve app stability through the migration by staying in the Dagger world. I’m also glad to be personally familiar with Hilt since it’s likely to stick around.

Happy coding!

Special thanks to Saad Farooq, Mustafa Isiker, and Shane Stafford (my Metromile Android teammates) for their guidance and feedback as we researched our options, and to Matthew Groves (my former colleague and mentor) for his excellent edits.

--

--