Dependency Injection with Koin and Kotlin in Android

There are a lot of reasons to use Dependency Injection, or even a Framework for it.

In this Article you’ll learn what Dependency Injection is, what benefits and downsides it has and how to use it with the usage of the Koin Framework.

Let’s look at a example of a House with a Doorbell to better understand what dependency injection is.

The House class has a function that returns a String representing the current Ringtone according to the installed doorbell.

Without Dependency Injection:

class House() {
    private val myDoorbell: Doorbell
   
   init {
       myDoorbell = SpeakingDoorbell()
   }
   
   fun ringDoorbell(): String {
       return "Current ringtone " + myDoorbell.getRingtone()
   }
}

fun main() {
    val myHouse = House()
   
    println(myHouse.ringDoorbell())
}

With Dependency Injection:

class House(private val myDoorbell: Doorbell) {
   fun ringDoorbell(): String {
       return "Current ringtone " + myDoorbell.getRingtone()
   }
}

fun main() {
    val myDoorbell: Doorbell = SpeakingDoorbell()
    val myHouse = House(myDoorbell)
   
    println(myHouse.ringDoorbell())
}

As you can see in the variant without Dependency Injection the House class has a direct dependency to the SpeakingDoorbell class.

With Dependency Injection this reference no longer exists. The only reference
remaining is to the generalized Doorbell Interface.

Benefits of Dependency Injection

Flexibility

Lets suppose another class FunnyDoorbell should be used in the future, which also implements the Doorbell interface.

Exchanging the SpeakingDoorbell class later on is less work with Dependency Injection.

In the first example the House class would have to change its implementation to switch to the FunnyDoorbell class.

But with Dependency Injection this change can be done without touching the House class.

In fact the House class does not even know whether it got an instance of SpeakingDoorbell or FunnyDoorbell.

This allows to write more flexible code which can be modified and extended easier.

A looser coupling between classes is achieved.

Testability

When writing tests one cannot test the House class in the first example in isolation to the SpeakingDoorbell class.

With Dependency Injection it’s very easy to insert mocks instead of the real instance.

Downsides of Dependency Injection

There is one part that got more complex though. The main function now knows not only the House class, but also the SpeakingDoorbell class.

This ultimatley leads to a really big main function, that knows almost every component.

In this small example this is no issue, but when developing large applications we want the benefits of Dependency Injection, but as few downsides as possible.

This is exactly where Dependency Injection Frameworks comes into play!

They help to structure the whole block how and when to instantiate which classes and usually bring with them a bunch of useful features.

So let us look into the Dependency Injection Framework Koin.

Setting up Koin

Gradle

Add the following to your gradle configuration.
Get the latest Koin version [here](https://insert-koin.io/docs/setup/v3).

// Add Maven Central to your repositories if needed
repositories {
    mavenCentral()    
}
dependencies {
    // Koin for Android
    implementation "io.insert-koin:koin-android:$koin_version"
}

Application

Create an application class if you have none yet (don’t forget to add it to the manifest).

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
     
        startKoin {
            androidContext(this@MyApplication)
            modules(
                module {
                    single { House(get()) }
                    single<Doorbell> { SpeakingDoorbell() }
                }
            )
        }
    } 
}

Profit

That’s it! This is the basic Koin setup. As you can see the House class now even fetches its needed instance of Doorbell.
Simply call startKoin, configure the AndroidContext and define some Beans. These can also be injected like this into Android components like activities:

val myVariable: MyVariableType by inject()

ViewModel injection

The avid reader might already have noticed something. “What about our ViewModels. They have a lifecycle and should not get created just like that”.

To fix this issue there exists an extension for Koin.

Instead of declaring a Bean like that

single { MyClass() }

we use

viewModel<MyViewModelType> { MyViewModel() }

The injection is then done inside the fragment like that:

val myViewModel: MyViewModelType by viewModel()

Conclusion

Using Dependency Injection, especially with a framework, can be intimidating for a new programmer.

But in the most cases the positive aspects outweigh the negatives and the complexity of setting up a dependency
injection framework is quite low.

There are a lot of other things Koin can do like defining factories instead of singles, directly helping out with tests, instantiating fragments, or much more.

Have a glance at the official documentation

How to fix android.view.InflateException: Error inflating class fragment

I got the error

E/AndroidRuntime: FATAL EXCEPTION: main
Process: de.creatronix.levelup, PID: 17026
java.lang.RuntimeException: Unable to start activity ComponentInfo{de.creatronix.levelup/de.creatronix.levelup.MainActivity}: android.view.InflateException: Binary XML file line #18: Error inflating class fragment

after enabling minification in my build.

The issue is that with the usage of safeargs with custom objects we use the the @parcelize annotation which seems to be optimized away with R8 Continue reading “How to fix android.view.InflateException: Error inflating class fragment”

What is kapt?

kapt is short for Kotlin annotation processor tool.

Why do I need it?

tl;dr; every time you use an annotation in a Kotlin file you need to use kapt.

Prerequisites

dependencies {
    kapt("groupId:artifactId:version")
}
plugins {
    kotlin("kapt") version "1.5.31"
}

Further reading

https://kotlinlang.org/docs/kapt.html

What is Ninja?

Ninja is a build system which replaces the GNU make in AOSP

  • Up to Marshmallow / 6,0 the build system was based on GNU Make
  • In Nougat / 7.0 and later, the build is run by ninja
  • Ninja works from a pre-processed manifest generated by kati and soong

kati

kati converts Android.mk files into ninja manifests

soong

soong converts blueprint files (Android.bp) into ninja manifests

Example bp file

https://android.googlesource.com/platform/system/core/+/refs/tags/android-11.0.0_r46/logcat/Android.bp

Further reading

https://ninja-build.org/

How can you use StrictMode in your Android app?

import android.os.StrictMode
import android.os.StrictMode.ThreadPolicy
import android.os.StrictMode.VmPolicy


class LevelUpApplication : Application() {
    override fun onCreate() {
        if (BuildConfig.DEBUG) {
            StrictMode.setThreadPolicy(
                ThreadPolicy.Builder()
                    .detectDiskReads()
                    .detectDiskWrites()
                    .detectNetwork()
                    .penaltyLog()
                    .build()
            )
            StrictMode.setVmPolicy(
                VmPolicy.Builder()
                    .detectLeakedSqlLiteObjects()
                    .detectLeakedClosableObjects()
                    .penaltyLog()
                    .penaltyDeath()
                    .build()
            )
        }
        super.onCreate()

https://developer.android.com/reference/android/os/StrictMode