Dagger 2 + Android + Kotlin. Mini HOWTO
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
и пару раз поменяйте ориентацию.
Документация где обычно: