Dependency Injection with Kotlin — #Kodein #KOIN

Filip Procházka
ProAndroidDev
Published in
5 min readMar 19, 2018

--

Dependency injection is an instrumental technique, used to decouple dependencies from your code. Today, I want to look into how we can implement it in Kotlin. I will focus mostly on usage in the Android environment, though both libraries I will mention, are not limited to this environment.

Before we dive into the code, I want to stop a little bit in which state we are now. If you have an app older than a year before Google announced the support for Kotlin, there is high probability it’s still in large part written in Java. And uses Dagger 2 for dependency injection (maybe you are not using DI at all, then god with you).

Before Kotlin came, Dagger was for quite some time the de-facto standard for DI in the Android world. Developed by Google, it uses annotations and compile-time code generation to create your dependency graph and provide dependencies. No blame here, I was also using it for quite some time, but as it happens, it’s a framework developed in Java world. With Kotlin in the light now, we have new features and time came for the Dagger replacement, which will utilize them.

Welcome KODEIN (KOtlin DEpendency INjection)

To borrow just a few lines from its website, it brings you the following:

  • small library, uses inline functions
  • DSL for dependency definition (no annotations, no code generation)
  • no type erasure

Sounds interesting to you? Let’s add it to our project!

Open your build.gradle file and add the following:

KODEIN extensively utilizes inline functions (so its nice not to get over your DEX limit).

Make our application KodeinAware

To make our dependencies accessible using application Context , we need to make our custom Application class implement KodeinAware interface.

Now we are ready to declare our bindings. So for that, let’s imagine a simple app. Don’t expect a lot of creativity; it will be a simple TODO list app. A single screen where you can add new tasks by pressing a button.

For this app, we will have a TasksRepository , which holds the tasks and allows us to store new.

Bindings and modularity

As you can see above, we are providing a binding of TasksRepository called FakeTasksRepository .

The way this works is that in our Kodein block, we start a binding with the keyword bind<T>() where T will be our bound type.

bind<TasksRepository>()

Then we continue and write with ,like if we were writing a sentence.

bind<TasksRepository>() with

and finally, a binding function. There are various types, for simplicity, we use singleton {} here. It lazyly calls the function block the first time you access our TasksRepository and then keeps the instance for the lifetime of your application (I will mention more binding functions below).

But we can do better, imagine, in more complicated app having all bindings in one file! As with Dagger, with KODEIN we can also split our app in modules. So let’s rewrite our binding into a Kodein.Module .

To include the binding in our application Kodein , we just have to import it

Retrieving dependencies

As we have successfully defined our TasksRepository , we can now retrieve it!

In the example above, we assume that we can directly access our kodein instance.

As you can see, thanks to type inference, we just call instance() function and receive the appropriate instance (or Kodein.NotFoundException if no binding is found).

Because we are on Android here, let’s see how we would do it in an Activity.

We have our Activity class, retrieving the TasksRepository instance. Thanks to some amazing Kotlin features like property delegation the retrieval is as easy (or even easier) as definition.

To access our Kodein instance, we implement the interface AppCompatActivityInjector (for AppCompatActivity , other available for Fragments and Services).

We provide new instance of KodeinInjector .

And finally, call initializeInjector() in our onCreate() and then destroyInjector() before onDestroy() .

This is important to make sure we will not introduce memory leaks into our application.

Now we can retrieve our dependencies.

Important note: As we are initializing our Activity in onCreate, we utilize property delegation, to lazyly access Kodein, which is not available at the time of instance creation of our Activity.

Other binding functions

Above we used singleton {} for providing our TasksRepository . Now let’s look into other options Kodein has.

Factory

It’s great simplification of classic ‘factory’ pattern. You provide an argument and new instance is created every time.

val kodein = Kodein {     
bind<Dice>() with factory { sides: Int -> RandomDice(sides) }
}

The way we retrieve such dependency is using with(6).instance() . Where the function with() provides the argument.

Note: if you need to access the argument you are passing lazyly, use the ‘lazy’ version of with { intent.getIntExtra("SIDES") }.instance()

Provider

A factory without an argument, thus, a provider.

val kodein = Kodein {     
bind<Dice>() with provider { RandomDice(6) }
}

Singleton

Already mentioned here, on first access invokes the function to obtain instance. That is then persisted during the life of the application.

val kodein = Kodein {     
bind<DataSource>() with singleton {
SqliteDS.open("path/to/file")
}
}

There are more of those, the abovementioned are just the most used.

Tagged bindings

Another nice thing use can utilize is tagging your bindings. Sometimes you need more than just one instance of specific service or just multiple bindings. With Kodein it’s very easy, just tag your bindings.

val kodein = Kodein {
bind<Dice>() with factory { sides: Int -> RandomDice(sides) }
bind<Dice>("DnD10") with provider { RandomDice(10) }
bind<Dice>("DnD20") with singleton { RandomDice(20) }
}

The tag doesn’t have to be String, it can be any non-null value.

Java interoperability

Because you may not have 100% pure Kotlin project by now, you may be interested in whether you can use Kodein and thus replace Dagger even with your Java code.

Yes, you can

The author of Kodein thought about us and added Java interoperability layer. You can’t define bindings, but you can retrieve your dependencies!

import static com.github.salomonbrys.kodein.TypesKt.TT; public class JavaClass {

private final Function1<Integer, Dice> diceFactory;
private final Datasource dataSource;
private final Function0<Random> randomProvider;
private final String answerConstant;
public JavaClass(Kodein kodein) {
diceFactory = kodein.Factory(
TT(Integer.class),
TT(Dice.class),
null
);
dataSource = kodein.Instance(
TT(Datasource.class),
null
);
}
}

With the use of static TT() function, you can retrieve the type argument to access your declared dependencies.

Summary

So I have shown you here an alternative to dependency injection, which heavily utilizes all the ‘cool’ Kotlin features and makes our lives happier.

There is an alternative to KODEIN called KOIN. It’s younger, doesn’t have the Java interoperability part, but on the other hand, it’s much slicker and supports the new ViewModel from Google out of the box.

In next blog post, I will show some more advanced features of Kodein, and we can also look into KOIN, and it’s comparison with KODEIN.

--

--

Published in ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Written by Filip Procházka

Czech, love travelling, cooking, developing mobile apps at Ubiquiti Networks

Responses (4)

Write a response