Kodein (2)


Kodein: Inherit parent dependencies

When you override Kodein instance in activity, fragment or somewhere else, you will lose all dependencies from parent scope.
To prevent this, there is a extend method

Example

In my App I have settings instance I want to use in Activity

 
class App : Application(), KodeinAware {

   override val kodein = Kodein {
     import(androidXContextTranslators)
     bind<ISettings>() with singleton { Settings(this@App) }
   }

Now in activity:

class MainActivity : Activity(), KodeinAware {

    private val appKodein by kodein(App.instance)

    override val kodein = Kodein {
       // or
       // extend(appApplication.appKodein(), allowOverride = true)
       extend(appKodein)
       // now you have all dependecies, you initialized in App
       //...
    }

    private val settings: ISettings by instance() // here we have it. Able to use settings instance



Dependency Injection with Kodein

If you build your Android application with Kotlin and want to implement DI technique then take a look at Kodein framework. Today I will take a quick look at it with you. I will not dive deep into dependency injection itself in this post. If you are not familiar with DI pattern, search articles on Dagger 2 for more info.
I will show Kodein practical usage on a simple example. Let’s say you want to inject singleton retrofit instance somewhere.

First, add Kodein dependency to the gradle file:

 implementation "org.kodein.di:kodein-di-generic-jvm:6.1.0"
 implementation "org.kodein.di:kodein-di-framework-android-core:6.1.0"
 implementation "org.kodein.di:kodein-di-framework-android-x:6.1.0"

Implement KodeinAware interface in your Application class. You will have to override kodein val and implement Retrofit instance there:

class App : Application(), KodeinAware {

    override val kodein = Kodein {
        import(androidXContextTranslators)
        bind<Retrofit>() with singleton {
            Retrofit.Builder()
                .client(OkHttpClient().newBuilder().build())
                .baseUrl("htttp://example.com")
                .addCallAdapterFactory(CoroutineCallAdapterFactory())
                .build()
        }
    }
}

All dependencies always starts with

 bind<TYPE>() with

followed by “singleton”, “provides” or “factory”
singleton” speaks for itself, “provides” generates new instance every time. If you need custom instance, then use “factory“; like”provides” it creates new instance every time but with applied constructor.

After declaring Dependency let’s use it!
You may need to inject Retrofit instance in Activity/Fragment or, if you use MVP pattern, in presenter. Difference is context: when in presenter you do not have context by default, you need to pass it. Let’s start with Activity case first.

Again, you need to imlpement KodeinAware interface and get the instance:

class MainActivity : AppCompatActivity, KodeinAware{

    override val kodein: Kodein by kodein()

    private val retrofit: Retrofit by instance()
    ...
    // use retrofit

Now, if you want to use retrofit in presenter or in some other class that is not aware of Android context,
first pass Context instance to get Kodein instance. I did it with App instance.
Add this into App class:

    ...
    override fun onCreate() {
        super.onCreate()
        instance = this
    }
    
    companion object {
        lateinit var instance: App
            private set
    }
    ...

Now in presenter:

class MyPresenterImpl : IMyPresenter, KodeinAware {

    override val kodein by kodein(App.instance)

    override val kodeinContext = kcontext(App.instance)

    private val retrofit: Retrofit by instance()

    ...
    // use retrofit

For more info check out official documentation