Почему Dagger 2 так популярен? Многие приложения для Android используют его. Даже Google выступал за использование Dagger 2. Но почему? Разработчики iOS задаются вопросом, некоторые разработчики Android просто используют его вслепую. Поэтому подумал, что мне стоит просто поделиться этим здесь ...

Примечание: это не учебник по Dagger 2, а скорее для общего понимания того, почему используется Dagger 2. Чтобы изучить Dagger 2 простым способом, обратитесь к Dagger 2 для чайников.

Проблема с Activity и Fragment

В Android мы используем Activity и Fragment для формирования представлений на экране. Это UIViewController iOS.

Для UIViewController мы можем отправлять зависимости через конструктор.

let uiController = MyViewController(dependency: myDependency)

Но в Android это НЕТ-НЕТ как для фрагментов, так и для действий.

В действии

Это очевидно, когда мы начинаем Activity, используя приведенное ниже. Он даже не вызывает конструктор. Невозможно передать какой-либо параметр.

startActivity(Intent(context, StartingActivity::class.java))

Во фрагменте

Здесь не все так однозначно. Мы сначала создаем экземпляр фрагмента перед его фиксацией.

val fragment = MyFragment()
fragmentManager.beginTransaction().add(container, fragment).commit()

Но если добавить параметр в like MyFragment(myArgument), то восстановление состояния обречено на гибель. Система не может восстановить эти аргументы.

Конечно, при желании мы могли бы отправлять данные, используя Intent для Activity и Bundle для Fragment, но это в основном для сериализуемых данных, а не для зависимостей.

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

Из-за этого нам нужно какое-то решение, чтобы получить наши зависимости не через конструктор. Activity и Fragment должны извлекать свои зависимости, а не подталкиваться.

Контейнер зависимостей, сложнее, чем кажется

На ум приходит быстрое решение: пусть что-нибудь хранит все наши зависимости. В Android у нас есть единое приложение, доступное для всех.

Интересно, что разработчики iOS также сейчас рассматривают этот подход, как сказано в PointFree, эпизод 16. Он рекомендует объект Singleton of Environment, который представляет собой контейнер, в котором хранятся все его основные зависимости.

Я думаю, что для простого приложения это было бы отличным подходом.

Когда количество зависимостей между приложениями растет, у нас не может быть только одного контейнера. Нам нужны способы разбить их на подконтейнеры для связанных зависимостей. Нам нужно подключить зависимости к зависимостям вручную. например

val interceptor = Container.providesInterceptor(context)
val okHttpClient = Container.providesOkHttpClient(interceptor)
val networkClient = Container.providesNetworkClient(okHttpClient)

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

Вскоре мы осознаем необходимость создания собственного механизма DI (зависимостей). И мы, вероятно, натолкнемся на этот блог ниже, похоже, это не так просто.



Если нет хорошего решения, возможно, нам следует подумать ... но зачем изобретать велосипед, если есть хорошее решение?

Ручной DI: код не такой аккуратный

Даже если бы у нас был элегантный способ создания контейнера для внедрения необходимых зависимостей, как бы мы получили к ним доступ?

  1. Прямой доступ через контейнер? например Container.networkClient
  2. Назначить локальной переменной перед ее использованием? то есть вручную подключать вещи, например. val networkClient = Container.networkClient

Хотя это приемлемо, они создают ненужные шаблоны, которые Dagger 2 помогает решить с помощью Field Injection.

Кинжал 2: все, что нужно, в основном на месте

Dagger 2 предоставляет отличные возможности DI, как показано ниже

Автоматический монтаж зависимостей

Вместо того, чтобы вручную подключать все зависимости. Ниже приведен один пример.

@Singleton
@Provides
fun providesInterceptor(context: Context): UserAgentInterceptor {
    val userAgent = "... some string ..."
    return UserAgentInterceptor(userAgent)
}
@Singleton
@Provides
fun providesOkhttpClient(
    interceptor: UserAgentInterceptor): OkHttpClient {
    val builder = OkHttpClient.Builder()
    builder.addNetworkInterceptor(userAgentInterceptor)
    setupOkhttpDebugInterceptor(builder)
    return builder.build()
}
@Singleton
@Provides
fun providesNetworkClient(okHttp: OkHttpClient) 
    = NetworkClient(okHttp)

Предполагая, что нам нужен NetworkClient. Нам просто нужно определить

@Inject
lateinit var networkClient: NetworkClient

Вместо того, чтобы писать это.

val interceptor = Container.providesInterceptor(context)
val okHttpClient = Container.providesOkHttpClient(interceptor)
val networkClient = Container.providesNetworkClient(okHttpClient)

Чистая группировка и область видимости зависимостей

Dagger 2 позволяет легко разделить связанные зависимости в разные контейнеры (например, Component, SubComponent, Module). Они могут быть разной продолжительности жизни (например, Scoping, Singleton).

Схема ниже иллюстрирует это более четко (обратите внимание, что раздел - это всего лишь пример. Его можно спроектировать по-разному в зависимости от потребностей приложения).

Мы могли бы сгруппировать зависимости Activity в рамках Activity Component. Их можно разделить на модули A и B.

Действие также может нуждаться в некоторых зависимостях от приложения, поэтому мы могли бы сделать его зависимым от компонента приложения (зависимости приложения могут быть помещены в другой модуль библиотеки пакетов, если мы хотим, что позволяет модульную разработку приложений).

Если мы захотим, Fragment также может иметь свои собственные зависимости, помимо того, что он получает от Activity. Вместо того, чтобы формировать компонент, мы могли бы сделать его субкомпонентами, которые обладают гибкостью, позволяющей легко получать доступ ко всем зависимостям своих родительских компонентов.

Имея такую ​​гибкость, разработчику просто нужно подумать, как спроектировать свои зависимости, и не беспокоиться о том, как их записывать.

Dagger 2: Хорошо протестирован, надежен и поддерживается

Просто погуглите Dagger 2, и вы сможете найти массу документации, руководств и поддержки, доступных в этой библиотеке. Есть даже курс Удеми на кинжале 2. Google выступает за использование Dagger 2. Он также разработан вслед за JSR330. Так что опоры точно есть.

Dagger 2 - это улучшенная версия от Dagger 1. Он больше не использует Reflection, что делает его время выполнения намного более эффективным, чем его предыдущее поколение. Сгенерированный код относительно минимален по сравнению с его предшественниками. (Примечание: тем не менее, мы должны изучить некоторые интересные способы дальнейшего сокращения сгенерированного кода, а также в этом блоге.)

Должен признать, кривая обучения Dagger 2 относительно крута для нового разработчика. Я удивлен, услышав от них известие, что даже некоторые ветераны все еще не могут понять и почувствовать, что Dagger 2 слишком волшебен. Но такая «трудная для изучения» библиотека, если бы не ее огромные преимущества, эффективность и возможности, она не просуществовала бы так долго.

Быстрая проверка некоторых популярных приложений, по крайней мере, на этот день, я мог видеть, что приложения Uber и Twitter используют Dagger 2. Slack, Facebook, SoundCloud, Trello и Reddit заявили, что Dagger находится в сторонней библиотеке. Это может относиться к Dagger 2. Я считаю, что многие приложения, разработанные Google, также являются Dagger 2 с DI.

Сказав так много о том, почему разработчику Android следует рассмотреть возможность использования Dagger 2, я ни в коей мере не против рассмотрения других восходящих фреймворков, например Коин. Я за лучший обладатель набора инструментов.

Я веду здесь блог, чтобы рассказать другим, почему Dagger 2 рассматривается для разработки под Android, и надеюсь избавить некоторых людей от страха перед использованием фреймворка из-за его крутой кривой обучения и, казалось бы, волшебной инъекции, происходящей за кулисами.

Мой опыт работы с кинжалами состоит из 3 этапов.

  1. НЕНАВИСТЬ: было так сложно научиться
  2. ЛЮБОВЬ + НЕНАВИСТЬ: это так волшебно.
  3. ЛЮБОВЬ: Понять, как это работает, и помочь архитектуре работать.

Если кто-то использует Dagger 2 только потому, что он волшебный, это только половина пользы. Магия - не лучший повод для использования инструмента, так как он придет и укусит нас, когда что-то больше не работает или не отвечает нашим особым потребностям.

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



Надеюсь, этот пост будет вам полезен. Вы можете ознакомиться с другими моими интересными темами здесь.

Следуйте за мной в medium, Twitter, Facebook или Reddit за небольшими советами и изучение Android, Kotlin и т. д., связанные темы. ~ Эли ~