Moving from Dagger to Koin — Simplify your Android development
The Koin framework has been started a while now (remember when I published its first lines in “Better dependency injection for Android”). I daily use Koin on several apps in production & begin to have some encouraging feedback (article updated with Koin version 0.9.2).
To show you how it can help you and save your time, let’s take a real app example — The TodoApp from Google Blueprint Architecture.
The Todo App
Google has published a Github repository for demoing Android development architecture, on a concrete app. The Todo app let you manage your todo list, in local or remote way (remote web service). The Google blueprints repository is available here and provides some declination of the app against architecture and technologies (MVP, MVVM, Dagger, Kotlin…).
I propose you to go with a “classical” MVP architecture, to compare Dagger and Koin approaches. Check the following repository for Todo App projects:
- Dagger 2 version : https://github.com/googlesamples/android-architecture/tree/todo-mvp-dagger
- Koin version: https://github.com/Ekito/android-architecture/tree/dev-todo-mvp-kotlin-koin
The Dagger project has been written by experts from the community. The Koin example project has been written by myself, inspired by the Google Kotlin version. Let’s go!
The application contains 4 self-describing packages, each one per view:
You can also find a data package for repository and datasources components. The di package is dedicated to dependency injection configuration. All of this is clearly identical in our two projects: you will find the same classes & packages in Dagger & Koin version.
We can clearly understand the application:
- list all todo
- add/edit a todo
- view a todo
- display statistics about todo
Google has opted for Activity/fragment per view: Activity is created, retrieve Fragment and Presenter. Fragment get the Presenter and Presenter need the TasksRepository.
We won’t go through all views, but we will rather just take the TasksActivity workflow example to illustrate how each framework is working. Before anything, each framework needs some Gradle dependencies.
Dagger has been configured with a bunch of dependencies (and of course need a extra compiler plugin):
Add Koin library with just one line:
As regular Android framework, we must start each one in our Android Application class. Our Dagger version is based on DaggerApplication class. The class below also ensure dependency for TaskRepository into Application class (also needed for test purpose here).
To start Koin, you just need to call ‘startKoin()’ function. No need for interface or any other special class. Also no need for special TaskRepository property injection.
The “todoAppModules” module just return a list of definitions, used in the application.
Now, to run the “Task list” view, we need to plug components like this:
In Dagger, all is annotation based and several module classes are needed to match different level of application context. The writing is consequently not straightforward, demanding us to write custom annotations (ActivityScope, FragmentScope).
We can simply do the same thing with Koin in a few lines. We just need to make one module (using applicationContext function), and use the Koin DSL to declare our components.
Koin DSL is composed of only 5 keywords :
- applicationContext — declare your Koin application context
- bean — declare a singleton instance component (unique instance)
- factory — declare a factory instance component (new instance on each demand)
- bind — declare an assignable class/interface to the provided component
- get — retrieve a component, for provided definition function
The function provided by components can use the “get” function, to lazily resolve dependency needed for your component constructor.
Both Koin and Dagger do context management: drop instances that don’t need to survive to some life cycle (Fragment, Presenters …) and keep singleton components (Repository, Datasources).
Ready to inject
The Dagger app version is obviously based on annotations (@Inject, @ActivityScoped — previously created in your project). DaggerAppCompatActivity and DaggerFragment are support classes to help the plumbing. Lazyness is specified with custom Lazy type.
Again, the same thing can be done very easily with Koin! 🙂 No need for annotation nor special class. To get your declared components with Koin, you can inject:
- into Android classes with “inject()” function (TasksActivity, TasksFragment)
- into class constructors (TasksPresenter, TasksRepository)
Koin is that simple: resolve components with inject() function or direct by constructor
Note that the “inject()” function is lazy by nature and bind a with a Kotlin val property.
Koin don’t require any special Android classes nor any introspection. No need to write custom annotation to match your need.
Every time you modify your injection dependency configuration, no need to regenerate & recompile all stuff behind (no need of Dagger generation). All is an entire part of your application source code (any modification/refactoring is applied to your Koin module).
Dependency injection, the Koin way
All the Google TodoApp has been converted with Koin framework (check the Github repo). No proxy/CGLib, no code generation, no introspection. Just functional Kotlin and DSL magic 😉
Using Koin in your project doesn’t impact in terms of memory or cpu. Profiling with Android Studio the Dagger & Koin version gives the same performances aspect.
Koin brings simple but powerful features and helps you to stay focused on the essential.
Koin offers a simple way of writing your apps:
- A DSL to simply describe your app with one or several modules
- Inject your instances via constructor or by “inject()” function
That’s it! No need to write anything else. All your app could be also described in one file — you to choose your modularity.
Scary about runtime dependency graph? Your Koin configuration can be checked with just a JUnit test and the dry run function.