Pull to refresh

«Dagger-Android & AAC» или «впихнуть невпихиваемое»

Development of mobile applications *Development for Android *Kotlin *
image


Сегодня хотелось бы поговорить о Dagger 2, в частности о dagger-android, Android Architecture Components, а так же о проблеме, с которой я столкнулся при их использовании. Наверное, пост не столько познавательный, сколько философский, сразу прошу не кидаться тапками, потому как причины создания поста есть (как минимум субъективные), о них расскажу под катом.


Долго размышлял, стоит ли писать этот пост, но внутреннее равновесие пошатнулось, а значит посту быть.


Дело вот в чем: наверняка многие из вас уже использовали вышеописанные инструменты в своих проектах, или хотя бы игрались с ними, дабы понять что это за зверь такой. Так и я наконец то пришел к тому, что надо бы использовать их в своем проекте.


Теперь о том, чего я хотел добиться с помощью этих инструментов:


  • Забыть о кошмаре со сменой конфигурации при помощи «живучих» вьюмоделей
  • Использовать Dagger для поставки зависимостей прямо во вьюмодель
  • Организовать Scopes для зависимостей
  • Использовать новомодный dagger-android

Теперь немного кода для лучшего понимания:


Application
class App : DaggerApplication() {

    override fun applicationInjector(): AndroidInjector<out App> = DaggerAppComponent.builder().create(this)

}

AppComponent
@Singleton
@Component(modules =
[
    (AndroidSupportInjectionModule::class),
    (AppModule::class),
    (ActivityBuilder::class)
])
interface AppComponent : AndroidInjector<App> {
    @Component.Builder
    abstract class Builder : AndroidInjector.Builder<App>()
}

ActivityBuilder
@Module
abstract class ActivityBuilder {

    @ActivityScope
    @ContributesAndroidInjector(modules = [MainActivityModule::class, FragmentProvider::class, RepositoryModule::class])
    abstract fun bindMainActivity(): MainActivity
}

RepositoryModule
@Module
class RepositoryModule {

    @Provides
    @ActivityScope
    fun provideSomeDataRepository(socket: SocketService, restApi: RestApi, feedDao: FeedDao, userSettings: UserSettings): SomeDataRepository
            = SomeDataRepositoryImpl(socket, restApi, feedDao, userSettings)

Инъекция ViewModelFactory
    @Inject
    lateinit var factory: MainViewModelFactory

    private lateinit var viewModel: MainViewModel

    viewModel = ViewModelProviders.of(this, factory)
    .get(MainViewModel::class.java)

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

ViewModel
class MainViewModelImpl @Inject constructor(someDataRepository: SomeDataRepository) : MainViewModel(), MainViewModel.Input, MainViewModel.Output

Так вот, насчет того списка, чего я хотел получить от всех этих манипуляций.


Всё хорошо, всё работает, только вот я заметил одну особенность (чуть позже она стала очевидной): После смены конфигурации Dagger создает зависимости заново.Если поставить breakpoint на init блок в SomeDataRepositoryImpl, то мы увидим, что при добавлении фрагмента с такой же зависимостью — во фрагмент передается экземпляр того же объекта, что был создан при создании активности (breakpoint не сработал). Но при перевороте экрана, мы будем наблюдать создание нового объекта (breakpoint сработал), который не дойдет до вьюмодели, потому как она уже имеет экземпляр этого объекта.


Из за небольшого опыта разработки с этими инструментами, было принято решение — спросить совета у других разработчиков, на что я получал ответы, что эта ситуация — норма, что сборщик мусора справится с этим бардаком, и всё будет нормально работать.


Отсылаясь к заголовку поста, про "невпихиваемое", это было вычитано где-то в issues по dagger (не цитата слово в слово, но смысл такой же): "Вы пытаетесь вобрать всё лучшее из этих двух инструментов, не думаю, что это возможно"


Спойлер

Конечно, можно не использовать даггер-андроид, и подавать зависимости прямо во вьюмодель, используя попутно AndroidViewModel(application: Applitaction), но так не пойдет, ведь вьюмодель не должна знать о классе Application. Для меня до сих пор загадка — зачем этот класс вообще существует. Может кто-то объяснит в комментариях?


Подводя итог ко всему выше сказанному, у меня один вопрос: Можно ли сделать то, что я хочу? Можно ли избавиться от этого неконтролируемого создания объектов даггера при смене конфигурации?


Буду рад ответам в комментариях, спасибо за внимание.

Tags:
Hubs:
Total votes 12: ↑12 and ↓0 +12
Views 4.4K
Comments Comments 17