Google добавил к Dagger 2 поддержку Android “из коробки”, и это ощутимо уменьшило количество бойлерплейта в простых приложениях.

Время от времени приходится создавать новые приложения, поэтому я решил обновить и опубликовать свою шпаргалку по использованию Dagger 2.

Исходники всего приложения здесь. Ниже - фрагменты для удобного копипаста.

Итак. Библиотеки.

implementation "com.google.dagger:dagger:$daggerVersion"
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
implementation "com.google.dagger:dagger-android:$daggerAndroidVersion"
implementation "com.google.dagger:dagger-android-support:$daggerAndroidVersion" // if you use the support libraries
kapt "com.google.dagger:dagger-android-processor:$daggerAndroidVersion" 

На момент написания этой заметки daggerVersion = '2.12' и daggerAndroidVersion = '2.13'

buildscript {
    ext.daggerVersion = '2.12'
    ext.daggerAndroidVersion = '2.13'

    ....
}

Обратите внимание, что используется kapt, потому что annotationProcessor работает только с исходниками на Java. Соответственно, нужно не забыть плагин:

apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'

Объект для экспериментов. Нужен исключительно для рассказа.

class DummyObject(var msg: String) {
    init {
        Log.d("happy", "dummy object $msg created ${hashCode()}")
    }
    fun ping() = Log.d("happy", "dummy object $msg  pinged ${hashCode()}")
}

Module - поставщики данных. Ничего нового.

@Module
class AndroidModule(context: Context) {
    private val context: Context = context.applicationContext

    @Provides
    @Singleton
    internal fun provideApplicationContext() = context

    @Provides
    @Singleton
    internal fun provideSensorManager() =
            context.getSystemService(SENSOR_SERVICE) as SensorManager
}
@Module
class MyModule {
    @Provides
    @Singleton
    @Named("Long Live")
    fun ProfideLongLive() = DummyObject("Long Live")

    @Provides
    @Named("One Time")
    fun provideOneTime() = DummyObject("One Time")
}

Activity и Service - потребители данных.

class MainActivity : DaggerAppCompatActivity() {
    @Inject
    @field:Named("Long Live")
    lateinit var longLive: DummyObject

    @Inject
    @field:Named("One Time")
    lateinit var oneTime: DummyObject

    ....

    override fun onResume() {
        super.onResume()

        longLive.ping()
        oneTime.ping()
    }
}

Обратите внимание, что класс унаследован от класса DaggerAppCompatActivity, и в классе больше нет явного обращения к Component и вызова инжектора. Это теперь спрятано “под капотом” Dagger‘а.

Также обратите внимание, что для аннотации поля @Named в Kotlin нужно явно указывать скоуп:

@field:Named("Long Live")

Для каждой Activity, в которой будет происходить инъекция, немобходимо создать Module и в нем объявить injector. Сам injector будет создан Dagger‘ом.

@Module
public abstract class MainActivityModule {
    @ContributesAndroidInjector
    abstract MainActivity contributeActivityInjector();
}

C Service все аналогично (родительский класс, отсутствие бойлерплейта внутри, класс-module):

class MyService : DaggerService() {
    @Inject
    lateinit var manager: SensorManager

    ....
}
@Module
public abstract class MyServiceModule {
    @ContributesAndroidInjector
    abstract MyService contributeServiceInjector();
}

Component выполняет ту же роль, что и раньше.

@Singleton
@Component(modules = arrayOf(
        AndroidInjectionModule::class,
        AndroidSupportInjectionModule::class,
        AndroidModule::class,
        MyModule::class,
        MainActivityModule::class,
        MyServiceModule::class
))
interface MyApplicationComponent : AndroidInjector<MyApplicatoin>

В списке модулей должны быть перечислены все модули нашего приложения (включая модули наших activity и servce‘ов), а также два дополнительных модуля из Dagger‘а: AndroidInjectionModule и AndroidSupportInjectionModule.

Сам component унаследован от AndroidInjector. Прописывать инжекторы для всех activity и service‘ов
приложения больше не нужно.

Базовый класс приложения (Application) наследуется теперь от DaggerApplication.

Не нужно больше самим делать механизм доступа к component, но его создание - все еще наша ответственность, и это нужно сделать в специальном методе (см. ниже).

Обратите внимание, что applicationInjector() должен вернуть лямбду, внутри которой создается экземпляр компонента и вызывается inject() для класса нашего приложения (не потеряйте этот inject!)

class MyApplicatoin : DaggerApplication() {
    override fun applicationInjector() = AndroidInjector<DaggerApplication> {
        DaggerMyApplicationComponent.builder()
                .androidModule(AndroidModule(this))
                .build()
                .inject(this)
    }

    ....
}

Пример приложения доступен на GitHub: https://github.com/JollyDroidNotebook/DaggerMiniHowoto

Если попробуете запустить, то отфильтруйте лог по тэгу happy и пару раз поменяйте ориентацию.

Документация где обычно: