Kodein DI on Android

You can use Kodein as-is in your Android project or use the util library kodein-android.

Kodein does work on Android as-is. The kodein-android extension adds multiple android-specific utilities to Kodein.
Using or not using this extension really depends on your needs.

Have a look at the Android demo project!

Install

How to use kodein-android:
  1. Add this line in your dependencies block in your application build.gradle file:

    implementation 'org.kodein.di:kodein-di-generic-jvm:6.5.5'
    implementation 'org.kodein.di:kodein-di-framework-android-???:6.5.5'

    Kodein offers support for:

    Barebone Android

    kodein-di-framework-android-core

    Android + Support library

    kodein-di-framework-android-support

    Android + AndroidX library

    kodein-di-framework-android-x

    Either kodein-di-generic-jvm or kodein-di-erased-jvm must be declared in addition to the Kodein-Android package.
    If you are using SupportFragment in your application, then you must use either the -support or the -x package.
  2. Declare the dependency bindings in the Android Application, having it implements KodeinAware.

    Example: an Android Application class that implements KodeinAware
    class MyApp : Application(), KodeinAware {
    	override val kodein by Kodein.lazy { (1)
    	    /* bindings */
    	}
    }
    1 Using Kodein.lazy allows you to access the Context at binding time.
    Don’t forget to declare the Application in the AndroidManifest.xml file!
  3. In your Activities, Fragments, and other context aware android classes, retrieve the Kodein object with the kodein function.

  4. Retrieve your dependencies!

Retrieving

Injection does not work on Android classes that are reified by the system (Activity, Fragment, etc.) due the fact that…​ they are reified by the system! Therefore, on such classes, you can either use retrieval, or if you want these classes to be independent of Kodein, use the dependency holder pattern.

Getting a Kodein object

You can always get the Kodein object by using:

  • kodein() inside an Android class (such as Context, Activity, Fragment, etc.)

  • kodein(context) or kodein { context } inside another class

The kodein function will only work if your Android Application class implements the KodeinAware interface.
The kodein result should be cached and not used multiple times in a row.

Being KodeinAware

It is very simple to have your Android classes be KodeinAware:

Example: a KodeinAware Android Activity
class MyActivity : Activity(), KodeinAware {

    override val kodein by kodein() (1)

    val ds: DataSource by instance()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ds.connect() (2)
        /* ... */
    }

}
1 Retrieving the application Kodein instance via context.
2 Because everything is lazy, the kodein AND ds instances will both be retrieved only when needed, which is at that time.

Using a Trigger

If you want all dependencies to be retrieved at onCreate, you can very easily use a trigger:

Example: using an trigger in a KodeinAware Android Activity
class MyActivity : Activity(), KodeinAware {

    override val kodein by kodein()

    override val kodeinTrigger = KodeinTrigger() (1)

    val ds: DataSource by instance()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        kodeinTrigger.trigger() (2)
        /* ... */
    }

}
1 Just create a trigger, and Kodein will automatically use it.
2 The kodein AND all dependencies will both be retrieved at that time.
Using this approach has an important advantage: as all dependencies are retrieved in onCreate, you can be sure that all your dependencies have correctly been retrieved, meaning that there were no non-declared dependency.
If you only use instance (no provider or factory), you can also be sure that there were no dependency loop.

View Models

To use Kodein, you need an Android context. For that, View Models need to implement AndroidViewModel.

It is very easy to use Kodein inside View Models:

If you prefer your View Models to be independant from Kodein, you can use a View Model Factory.
Example: using an trigger in a KodeinAware Android Activity
class MyViewModel(app: Application) : ApplicationViewModel(app), KodeinAware {

    override val kodein by kodein() (1)

    val repository : Repository by instance()
}
1 Retrieving the application’s Kodein container.

Android module

Kodein-Android proposes a Module that enables easy retrieval of a lot of standard android services.

This module is absolutely optional, you are free to use it or leave it ;).
Example: importing the android module
class MyApplication : Application(), KodeinAware {
    override val kodein by Kodein.lazy {
        import(androidXModule(this@MyApplication)) (1)
	    /* bindings */
    }
}
1 Can either be androidXModule or androidSupportModule or androidCoreModule.

You can see everything that this module proposes in the Kodein-Android module.kt file.

Example: using kodein to retrieve a LayoutInflater
class MyActivity : Activity(), KodeinAware {
    override val kodein by kodein()
    val inflater: LayoutInflater by instance() (1)
}

If you are retrieving these classes inside a non-Android class, you need to define an Android Context as a Kodein context:

Example: using kodein with a context to retrieve a LayoutInflater
val inflater: LayoutInflater by kodein.on(getActivity()).instance()

or

Example: using kodein with a class context to retrieve a LayoutInflater
class MyUtility(androidContext: Context) : KodeinAware {

    override val kodein by androidContext.kodein()

    override val kodeinContext = kcontext(androidContext) (1)

    val inflater: LayoutInflater by instance()
}
1 Defining the default context: the Android context to use to retrieve Android system services.

Android context translators

The android module provides a number of context translators. For example, they allow you to retrieve an activity scoped singleton inside a fragment, without manually specifying the activity.

The android modules automatically register these translators.

However, if you don’t want to use the android modules, but still need these translators, you can register them easily:

Example: importing the android module
class MyApplication : Application(), KodeinAware {
    override val kodein by Kodein.lazy {
        import(androidXContextTranslators) (1)
	    /* bindings */
    }
}
1 Can either be androidXContextTranslators or androidSupportContextTranslators or androidCoreContextTranslators.

Android scopes

Component scopes

Kodein provides a standard scope for any component (Android or not). The WeakContextScope will keep singleton and multiton instances as long as the context (= component) lives.

Example: using an Activity scope
val kodein = Kodein {
    bind<Controller>() with scoped(WeakContextScope.of<Activity>()).singleton { ControllerImpl(context) } (1)
}
1 context is of type Activity because we are using the WeakContextScope.of<Activity>().
WeakContextScope is NOT compatible with ScopeCloseable.

Activity retained scope

Kodein-Android provides the ActivityRetainedScope, which is a scope that allows activity-scoped singletons or multitons that are independent from the activity restart.

This means that for the same activity, you’ll get the same instance, even if the activity restarts.

This means that you should never retain the activity passed at creation because it may have been restarted and not valid anymore!
Example: using an Activity retained scope
val kodein = Kodein {
    bind<Controller>() with scoped(ActivityRetainedScope).singleton { ControllerImpl() }
}
This scope IS compatible with ScopeCloseable: see documentation.

Lifecycle scope

Kodein-Android provides the AndroidLifecycleScope, which is a scope that allows activity-scoped singletons or multitons that are bound to a component lifecycle. It uses Android support Lifecycle, so you need to use Android support’s LifecycleOwner components.

Example: using an Activity retained scope
val kodein = Kodein {
    bind<Controller>() with scoped(AndroidLifecycleScope<Fragment>()).singleton { ControllerImpl(context) }
}
These lifecycles are NOT immune to activity restart due to configuration change.
This scope IS compatible with ScopeCloseable: see documentation.

Layered dependencies

The closest Kodein pattern

Android components can be thought as layers. For example, a View defines a layer, on top of an Activity layer, itself on top of the Application layer.

The kodein function will always return the kodein of the closest parent layer. In a View or a Fragment, for example, it will return the containing Activity’s Kodein, if it defines one, else it will return the "global" Application Kodein.

In the following code example, if MyActivity contains Fragments, and that these fragments get their Kodein object via kodein(), they will receive the MyActivity Kodein object, instead of the Application one.

Component based sub Kodein

In Android, each component has its own lifecycle, much like a "mini application". You may need to have dependencies that are defined only inside a specific component and its subcomponents (such as an activity). Kodein allows you to create a Kodein instance that lives only inside one of your components:

Example: defining an Activity specific Kodein
class MyActivity : Activity(), KodeinAware {

    override val kodein by subKodein(kodein()) { (1)
        /* activity specific bindings */
    }

}
1 Creating a sub Kodein container that is valid for this activity and all components of this activity.

By default all bindings are cached. You can also define the way the parent kodein is extended by defining the copy mode. In below example each instance of activity will store a copy of the kodein module (all bindings including singleton will be recreated per activity instance).

Example: defining an Activity specific Kodein that copies all parent bindings
override val kodein by subKodein(kodein(), copy = Copy.All) {
    /* component specific bindings */
}

Activity retained sub Kodein

Kodein-Android provides retainedSubKodein for Activities. It creates a Kodein object that is immune to activity restarts.

This means that you should never access the containing activity it may have been restarted and not valid anymore!
Example: defining an Activity specific Kodein
class MyActivity : Activity(), KodeinAware {

    override val kodein: Kodein by retainedSubKodein(kodein()) { (1)
        /* activity specific bindings */
    }

}
1 Using retainedSubKodein instead of subKodein ensures that the Kodein object is retained and not recreated between activity restarts.

You can define the way the parent kodein is extended by defining the copy mode:

Example: defining an Activity specific Kodein that copies all parent bindings
override val kodein by retainedSubKodein(kodein(), copy = Copy.All) {
    /* component specific bindings */
}

Independant Activity retained Kodein

Kodein provides the retainedKodein function that creates a Kodein instance that is independendant from the parent.

This means that all bindings in the application context are NOT available through this new Kodein.
Example: defining an independant Kodein Container.
class MyActivity : Activity() {

    val activityKodein: Kodein by retainedKodein { (1)
        /* activity specific bindings */
    }

}

Kodein in Android without the extension

Being KodeinAware

It is quite easy to have your Android components being KodeinAware (provided that your Application class is KodeinAware).

Using lazy

Example: a KodeinAware Activity
class MyActivity : Activity(), KodeinAware {
    override val kodein: Kodein by lazy { (applicationContext as KodeinAware).kodein }
}

Using lateinit

Example: a KodeinAware Activity
class MyActivity : Activity(), KodeinAware {
    override lateinit var kodein: Kodein
    override fun onCreate(savedInstanceState: Bundle?) {
        kodein = (applicationContext as KodeinAware).kodein
    }
}

Using LateInitKodein

If you don’t want the component classes to be KodeinAware, you can use a LateInitKodein:

Example: an Activity with LateInitKodein
class MyActivity : Activity() {
    val kodein = LateInitKodein()
    override fun onCreate(savedInstanceState: Bundle?) {
        kodein.baseKodein = (applicationContext as KodeinAware).kodein
    }
}

Being Kodein independant

The dependency holder pattern

If you want your components to be Kodein-independent, you can use the dependency holder pattern:

Example: The dependency holder pattern
class MyActivity : Activity() {

    class Deps(
            val ds: DataSource,
            val ctrl: controller
    )

    val deps by lazy { (applicationContext as MyApplication).creator.myActivity() }

    val ds by lazy { deps.ds }
    val ctrl by lazy { deps.ctrl }

    /* ... */
}

class MyApplication : Application() {

	interface Creator {
	    fun myActivity(): MyActivity.Deps
	}

	val creator: Creator = KodeinCreator()

    /* ... */
}

class KodeinCreator : MyApplication.Creator {

    private val kodein = Kodein {
        /* bindings */
    }.direct

    override fun myActivity() = kodein.newInstance { MyActivity.Deps(instance(), instance()) }
}

View Model Factory

If you want your view models to be independant from Kodein, then you need to inject them (meaning passing their dependencies by constructor). To do that, you need to create your own ViewModelProvider.Factory.

Here is a simple one:

A Kodein View Model Factory
class KodeinViewModelFactory(val kodein: Kodein) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T =
            kodein.direct.Instance(TT(modelClass))
}