Dependency Injection (1)


Dependency Injection с Kodein

Если вы пишите Android-приложение на Kotlin и хотите использовать инъекцию зависимостей, взгляните на платформу Kodein. Она создана специально для Android и Kotlin. Не буду глубоко погружаться в паттерн dependency injection в этом посте. Если вы с ним не знакомы, рекомендую загуглить Dagger 2 для ознакомления.
Сегодня мы вместе с Вами взглянем на Kodein в действии на примере. Предположим, мы хотим занижектить singleton инстанс retrofit-а в каком-либо месте нашего приложения.

Сначала, добавим Kodein зависимости в gradle файл:

 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"

Имплементим интерфейс KodeinAware в классе приложения Application. Будет необходимо переопределить kodein val и в нем инициализировать инстанс Retrofit-а:

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()
        }
    }
}

Все зависимости внутри блока Kodein формируются по следующему паттерну:

 bind<TYPE>() with

далее идет метод создания и использования объекта: «singleton», «provides» or «factory»
«singleton» говорит сам за себя, «provides» каждый раз генерит новый объект. Если нужен кастомный объект, тогда используйте фабрику: «factory«; как и «provides» он создает каждый раз новый объект, но с конструктором.

Теперь, когда мы объявили зависимости, давайте использовать их!
Заинжектим наш инстанс Retrofit-а в Activity/Fragment или, если вы используете MVP паттерн, то в presenter. Разница в наличии контекста: в презентере его нет по умолчанию. Попробуем сначала внедрить Retrofit в Activity.

И снова заимплементим интерфейс KodeinAware и получим инстанс:

class MainActivity : AppCompatActivity, KodeinAware{

    override val kodein: Kodein by kodein()

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

А теперь, если мы хотим использовать ретрофит в классе без Android контекста,
то нам нужно всего лишь передать туда Context для того, чтобы создать инстанс Kodein. Для этого я использовал applicationContext.
Добавьте следующие строки в класс Application:

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

И теперь в презентере:

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

Ссылка на официальную документацию