Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Toothpick: наше спасение от Dagger 2

GDG SPb
September 30, 2017

Toothpick: наше спасение от Dagger 2

Я покажу как опять полюбить DI и как настроить зависимости в приложении легко, красиво и правильно вместе с Toothpick. Пожалуюсь на боль от Dagger 2. И покажу как мигрировать на светлую сторону!

GDG SPb

September 30, 2017
Tweet

More Decks by GDG SPb

Other Decks in Programming

Transcript

  1. Ты кто такой? Константин Цховребов Senior Android Team Lead RedMadRobot

    Более 6 лет в Android. Работал в ooApps, Phereo, i-Free, MobileUp. С мая 2017 г. – ведущий Android-разработчик в RedMadRobot. В 2015 году стал победителем конкурса Павла Дурова для Android- разработчиков «Telegram Challenge». Выступал на конференциях AppsConf 2016, DevFest Siberia 2016, Mobius 2017.
  2. Проблема отсутствия DI • чтобы быть уверенным, как работает класс,

    надо найти все его зависимости • сложнее его переиспользовать или заменить
  3. Проблема отсутствия DI • чтобы быть уверенным, как работает класс,

    надо найти все его зависимости • сложнее его переиспользовать или заменить • сложнее протестировать
  4. Проблема отсутствия DI • чтобы быть уверенным, как работает класс,

    надо найти все его зависимости • сложнее его переиспользовать или заменить • сложнее протестировать • сложнее анализировать утечки
  5. Стандарт отрасли Dagger 2 • JSR-330 • отсутствует рефлексия •

    большие возможности • небольшой размер библиотеки
  6. Стандарт отрасли Dagger 2 • JSR-330 • отсутствует рефлексия •

    большие возможности • небольшой размер библиотеки • Square, а теперь Google
  7. Каждый делает по-своему • инъекции в поля • избегание Scope

    • хранение Component • разбиение на модули
  8. Каждый делает по-своему • инъекции в поля • избегание Scope

    • хранение Component • разбиение на модули • в последних версиях можно делать bind- адаптеры
  9. Как я стал срезать углы • для всего модули и

    provide-методы 0) возможность скрывать реализацию интерфейсом
  10. Как я стал срезать углы • для всего модули и

    provide-методы 0) возможность скрывать реализацию интерфейсом 1) наглядные зависимости внутри Component
  11. Как я стал срезать углы • для всего модули и

    provide-методы 0) возможность скрывать реализацию интерфейсом 1) наглядные зависимости внутри Component 2) Scope только для provide-методов
  12. Как я стал срезать углы • для всего модули и

    provide-методы 0) возможность скрывать реализацию интерфейсом 1) наглядные зависимости внутри Component 2) Scope только для provide-методов • избегание локального Component (далее)
  13. Как я стал срезать углы • для всего модули и

    provide-методы 0) возможность скрывать реализацию интерфейсом 1) наглядные зависимости внутри Component 2) Scope только для provide-методов • избегание локального Component (далее) • в реальности стал использовать инъекции зависимостей только в Activity/Fragment/Service...
  14. ChatPresenter (JSR-330) class ChatPresenter @Inject constructor( private val interactor: ChatInteractor,

    private val router: Router, @ChatId private val chatId: Int ) { //... }
  15. Как с этим справится Dagger? AppComponent 1. модуль с provide-методом

    для Router 2. интерфейс AppComponent 3. билдер компонента 4. сохранить ссылку на AppComponent
  16. Как с этим справится Dagger? AppComponent 1. модуль с provide-методом

    для Router 2. интерфейс AppComponent 3. билдер компонента 4. сохранить ссылку на AppComponent SessionComponent 1. Аннотация для SessionScope 2. модуль с provide-методом для ChatInteractor 3. интерфейс SessionComponent 4. билдер саб-компонента 5. сохранить ссылку на SessionComponent
  17. Как с этим справится Dagger? AppComponent 1. модуль с provide-методом

    для Router 2. интерфейс AppComponent 3. билдер компонента 4. сохранить ссылку на AppComponent SessionComponent 1. Аннотация для SessionScope 2. модуль с provide-методом для ChatInteractor 3. интерфейс SessionComponent 4. билдер саб-компонента 5. сохранить ссылку на SessionComponent ChatComponent 1. модуль с provide-методом для chatId 2. интерфейс ChatComponent 3. билдер саб-компонента
  18. Как с этим справится Dagger? AppComponent 1. модуль с provide-методом

    для Router 2. интерфейс AppComponent 3. билдер компонента 4. сохранить ссылку на AppComponent SessionComponent 1. Аннотация для SessionScope 2. модуль с provide-методом для ChatInteractor 3. интерфейс SessionComponent 4. билдер саб-компонента 5. сохранить ссылку на SessionComponent ChatComponent 1. модуль с provide-методом для chatId 2. интерфейс ChatComponent 3. билдер саб-компонента Boilerplate
  19. ScreenPresenter class ScreenPresenter @Inject constructor( private val interactor: ScreenInteractor, private

    val router: Router, private val menuController: MenuController, private val screenParams: ScreenParams ) { //... } interface MenuController { fun open() fun close() }
  20. Грязные хаки class ScreenPresenter( private val screenParams: ScreenParams ) {

    @Inject lateinit var interactor: ScreenInteractor @Inject lateinit var router: Router @Inject lateinit var menuController: MenuController init { DI.someComponent?.inject(this) } //... }
  21. Modules and Bindings class AppModule : Module() { init {

    bind(IFoo::class.java).to(Foo::class.java) //case 1 bind(IFoo::class.java).toInstance(Foo()) //case 2 bind(IFoo::class.java).toProvider(FooProvider::class.java) //case 3 bind(IFoo::class.java).toProviderInstance(FooProvider()) //case 4 bind(Foo::class.java) //case 5 } }
  22. Local singletons class AppModule : Module() { init { bind(IFoo::class.java)

    .to(Foo::class.java) .instanceInScope() //case 1 bind(IFoo::class.java) .to(Foo::class.java) .singletonInScope() //case 2 bind(IFoo::class.java) .to(FooProvider::class.java) .providesSingletonInScope() //case 3 } }
  23. Scope annotation @Qualifier annotation class ContextSingleton @ContextSingleton class Foo {}

    Toothpick .openScope("AppScope") .bindScopeAnnotation(ContextSingleton::class.java)
  24. Настройка • Runtime checks • Reflection • Multiple root detection

    Toothpick.setConfiguration( Configuration.forDevelopment().preventMultipleRootScopes() )
  25. Как с этим справится Dagger? AppComponent 1. модуль с provide-методом

    для Router 2. интерфейс AppComponent 3. билдер компонента 4. сохранить ссылку на AppComponent SessionComponent 1. Аннотация для SessionScope 2. модуль с provide-методом для ChatInteractor 3. интерфейс SessionComponent 4. билдер саб-компонента 5. сохранить ссылку на SessionComponent ChatComponent 1. модуль с provide-методом для chatId 2. интерфейс ChatComponent 3. билдер саб-компонента Boilerplate
  26. Toothpick .openScope("AppScope") .installModules(object : Module() { init { bind(Router::class.java).singletonInScope() }

    }) Toothpick .openScopes("AppScope", "SessionScope") .installModules(object : Module() { init { bind(ChatInteractor::class.java).singletonInScope() } }) Toothpick .openScopes("SessionScope", "ChatScope") .installModules(object : Module() { init { bind(Long::class.java).toInstance(42L) } })
  27. Миграция Dagger 2 -> Toothpick • Модули -> модули :)

    • Компонент -> скоуп • Удаляем билдеры компонентов
  28. Миграция Dagger 2 -> Toothpick • Модули -> модули :)

    • Компонент -> скоуп • Удаляем билдеры компонентов • Теперь не надо хранить ссылки на компоненты
  29. Миграция Dagger 2 -> Toothpick • Модули -> модули :)

    • Компонент -> скоуп • Удаляем билдеры компонентов • Теперь не надо хранить ссылки на компоненты • Не забываем убивать скоупы
  30. Миграция Dagger 2 -> Toothpick • Модули -> модули :)

    • Компонент -> скоуп • Удаляем билдеры компонентов • Теперь не надо хранить ссылки на компоненты • Не забываем убивать скоупы • Настройка перед релизом
  31. Спасибо за внимание! Toothpick https://github.com/stephanenicolas/toothpick OpenSource Android GitLab client (MVP,

    Moxy, Cicerone, Toothpick, Kotlin...) https://gitlab.com/terrakok/gitlab-client https://t.me/Android_Architecture - Android Architecture Chat https://t.me/androiddevpodcast - Android Ru Podcast github: https://github.com/terrakok telegram: @terrakok