Сегодня хотелось бы поговорить о Dagger 2, в частности о dagger-android, Android Architecture Components, а так же о проблеме, с которой я столкнулся при их использовании. Наверное, пост не столько познавательный, сколько философский, сразу прошу не кидаться тапками, потому как причины создания поста есть (как минимум субъективные), о них расскажу под катом.
Долго размышлял, стоит ли писать этот пост, но внутреннее равновесие пошатнулось, а значит посту быть.
Дело вот в чем: наверняка многие из вас уже использовали вышеописанные инструменты в своих проектах, или хотя бы игрались с ними, дабы понять что это за зверь такой. Так и я наконец то пришел к тому, что надо бы использовать их в своем проекте.
Теперь о том, чего я хотел добиться с помощью этих инструментов:
- Забыть о кошмаре со сменой конфигурации при помощи «живучих» вьюмоделей
- Использовать Dagger для поставки зависимостей прямо во вьюмодель
- Организовать Scopes для зависимостей
- Использовать новомодный dagger-android
Теперь немного кода для лучшего понимания:
class App : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out App> = DaggerAppComponent.builder().create(this)
}
@Singleton
@Component(modules =
[
(AndroidSupportInjectionModule::class),
(AppModule::class),
(ActivityBuilder::class)
])
interface AppComponent : AndroidInjector<App> {
@Component.Builder
abstract class Builder : AndroidInjector.Builder<App>()
}
@Module
abstract class ActivityBuilder {
@ActivityScope
@ContributesAndroidInjector(modules = [MainActivityModule::class, FragmentProvider::class, RepositoryModule::class])
abstract fun bindMainActivity(): MainActivity
}
@Module
class RepositoryModule {
@Provides
@ActivityScope
fun provideSomeDataRepository(socket: SocketService, restApi: RestApi, feedDao: FeedDao, userSettings: UserSettings): SomeDataRepository
= SomeDataRepositoryImpl(socket, restApi, feedDao, userSettings)
@Inject
lateinit var factory: MainViewModelFactory
private lateinit var viewModel: MainViewModel
viewModel = ViewModelProviders.of(this, factory)
.get(MainViewModel::class.java)
class MainViewModelFactory @Inject constructor(private val someDataRepository: SomeDataRepository) : ViewModelProvider.Factory{
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
return MainViewModelImpl(someDataRepository) as T
}
throw IllegalArgumentException(String.format("%s Not Found", modelClass.simpleName))
}
}
class MainViewModelImpl @Inject constructor(someDataRepository: SomeDataRepository) : MainViewModel(), MainViewModel.Input, MainViewModel.Output
Так вот, насчет того списка, чего я хотел получить от всех этих манипуляций.
Всё хорошо, всё работает, только вот я заметил одну особенность (чуть позже она стала очевидной): После смены конфигурации Dagger создает зависимости заново.
Из за небольшого опыта разработки с этими инструментами, было принято решение — спросить совета у других разработчиков, на что я получал ответы, что эта ситуация — норма, что сборщик мусора справится с этим бардаком, и всё будет нормально работать.
Отсылаясь к заголовку поста, про "невпихиваемое", это было вычитано где-то в issues по dagger (не цитата слово в слово, но смысл такой же): "Вы пытаетесь вобрать всё лучшее из этих двух инструментов, не думаю, что это возможно"
Конечно, можно не использовать даггер-андроид, и подавать зависимости прямо во вьюмодель, используя попутно AndroidViewModel(application: Applitaction), но так не пойдет, ведь вьюмодель не должна знать о классе Application. Для меня до сих пор загадка — зачем этот класс вообще существует. Может кто-то объяснит в комментариях?
Подводя итог ко всему выше сказанному, у меня один вопрос: Можно ли сделать то, что я хочу? Можно ли избавиться от этого неконтролируемого создания объектов даггера при смене конфигурации?
Буду рад ответам в комментариях, спасибо за внимание.