KOtlin Dependency Injection (KODI)

Alexandr Minkin
5 min readMay 18, 2020

Hello developers. Everybody now uses some dependency injection into your project. Some of them have annotation processing some of them use kapt-plugin or reflection, but now I want to tell you how was written my own dependency injection framework with few lines of code and implementations. I wrote some DI-containers before with different realizations and I had used a lot of “ready for production” frameworks such as Dagger, Kodein, Koin, Lightsaber, Toothpick, etc. But no one of them doesn’t satisfy my needs. And I think, what if I create my own cool Service Locator realization by Kotlin language. This is how KODI was born. The first version has used the kotlin-reflection library and it was good. So I start to think about independent module without reflection with all Kotlin language features like infix functions, inline classes, extensions, and delegations. Here will describe how I made this!

API Syntaxis

First of all, choose the right human syntax. I really love how Kodein names this, so I going to imitate its syntax.
bind<Instance>(tag: String?) at "ScopeName" with single { InstanceImplementation() } this is how you can create your instance binder to DI-container. bind is an inline extension function that creates inline class KodiTagWrapper that holds a key for DI-reference. with is an another cool Kotlin feature called infix operator. And then it has three types of binding: single — mean that only one instance of an initialized class will be binded. It’s also a lazy function; providerprovide called function or object reference every time its executed; constant — for the constant values. You can bind instances into DI-graph by its class name like bind<MyClass> or by tag bindTag(“MyClassTag”) . To start to initialize binding into containers you need to be in the context of Kodi framework by calling kodi { } and start to declare bind functions into including lambda expressions. There is a simple example of how to do it below.

Another one great feature is a SCOPES with key at, which tells the instance namespaces. By default, all dependencies have default scope, but you can specify it explicitly as a string constant, and retrieve it later in any part of your code with instance<T>(tag: String?, scope: String?) .

This all that you need to do to create a simple dependency graph. Moreover, it doesn’t matter what order you assign the dependencies to, they will all be injected lazily. It’s can be done in MainApplication class or KodiModule, which I’ll tell you more about it later.

Injection Methods

As I said earlier, the KODI does not use any annotation processing or reflection library until version 1.4.+. Only pure Kotlin code. So this is so simple to inject your dependencies into target class. Just implement IKodi.kt interface to get all features of KODI injection. Next code snippet will show you how to inject your instances with delegation properties in Kotlin.

  1. - You can get an instance by lazy immutableInstance , that get value only for val property, which is a part of KODI Framework. Also, you can get mutableInstance for var . It’s always lazy read-write properties.
  2. Directly get selected class to be injected into the property from DI-Container

This is a really simple way to get all your dependencies in any part of your code.

Kodi Modules

Every big project has separated modules in it like domain-logic module or data module, or features modules, etc. KodiModules can help you to manage your project architecture in a CLEAN way. There had been developed a great feature called KodiModule . This is a simple container for all of your bindings into the dependency graph. Every module can have SCOPE names too. So look at the example below:

As you can see the way to create a module is to call high-order function kodiModule {…} and bind all necessary dependencies in it. Also, you can use the module scope name withScope “some_const_string” to specify a scope. It will automatically be added for all instances inside this module without inner SCOPE declarations with at keyword. Next step to add a module into your main project graph is to call import function,- kodi { import(systemModule) } declarations. Import can be used in another module that’s mean that we can combine modules with each other.

AnnotationProcessing

Next big thing in KODI that no one Kotlin dependency injection frameworks have (Koin & Kodein), - is annotation processing with kapt. There are a few annotations that exist in KODI framework that can generate modules for you: @BindSingle, @BindProvider, @IgnoreInstance, @WithInstance

@BindSingle and @BindProvider works only with classes(statics and singletons) and methods (abstract and high-order) to automatically generate bind<T>() methods into modules with holder methods like single{} and provider{}. IgnoreInstance and WithInstance work properties and method params. @IgnoreInstance the annotation will ignore some instances to inject into your module binding. Using @WithInstance annotation you can specify what type of instance you want to inject into field or property. You need to implement kodigen library for kapt annotation processing. Let’s look at the example:

No boilerplate code anymore. It also reduces the chance of making a mistake if your dependencies and their input parameters change or are removed in the future. We can use annotations with Kotlin singleton object methods and higher-order functions.

You can use abstract methods, companion object methods and interface methods for binding injections into the dependency graph. Here was a simple example of how to use this framework and you can find a full documented code into my repository with an example of usage in the typical android application. It’s been over 2 years since the first version of this framework was released. Now I have three modules in the project:
- kodi. This is the main core framework module, which contains base abstractions, implementations, and annotations.
- androidX. This module was created only for usage in android development. It contains all services that android framework has used since version SDK 14.
- kodigen. Module for generating code via annotation processing for KodiModules.
- kodireflect. Old legacy module created for use with Kotlin reflection without any bindings and have less boilerplate code style.

KODI is a constantly growing library with simple usage syntaxis. Just add an implementation to your project and try it out:
implementation 'com.rasalexman.kodi:kodi:x.y.z'

and for annotation processing use:
kapt 'com.rasalexman.kodigen:kodigen:x.y.z'

Thanks for reading this article. Make some claps if you like it and please support me on github. Have a good day and write a clean readable code :)

--

--

Alexandr Minkin

Lead Software Developer. Kotlin enthusiast, Android developer, Swift lover, Clean Architecture Supporter