
Сегодня хотелось бы поговорить о 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. Для меня до сих пор загадка — зачем этот класс вообще существует. Может кто-то объяснит в комментариях?
Подводя итог ко всему выше сказанному, у меня один вопрос: Можно ли сделать то, что я хочу? Можно ли избавиться от этого неконтролируемого создания объектов даггера при смене конфигурации?
Буду рад ответам в комментариях, спасибо за внимание.
