Dagger 2 для начинающих Android разработчиков. Dagger 2. Часть 2
Это четвертая статья цикла «Dagger 2 для начинающих Android разработчиков». Если вы не читали предыдущие, то можете найти их здесь: первая статья, вторая статья, третья статья.
Ранее в цикле статей
В предыдущей статье мы обсуждали, как ручное использование внедрения зависимостей (DI) усложняет работу и увеличивает количество шаблонного кода. После рассмотрели, как Dagger 2 избавляет нас от этой боли и генерирует шаблонный код за нас. Также пробежались по обработчикам аннотаций и базовым аннотациям Dagger 2. Затем применили эти аннотации на примере и внедрили зависимости с помощью Dagger 2.
Анатомия DaggerBattleComponent
Для лучшего понимания Dagger 2 рассмотрим класс DaggerBattleComponent
. Установите курсор на DaggerBattleComponent
и нажмите Ctrl+B (или Ctrl+ЛКМ, Command+ЛКМ). Вы увидите примерно следующее:
@Generated( value = "dagger.internal.codegen.ComponentProcessor", comments = "https://google.github.io/dagger" ) public final class DaggerBattleComponent implements BattleComponent { private DaggerBattleComponent(Builder builder) {} public static Builder builder() { return new Builder(); } public static BattleComponent create() { return new Builder().build(); } // реализованный метод интерфейса @Override public War getWar() { return new War(new Starks(), new Boltons()); } public static final class Builder { private Builder() {} public BattleComponent build() { return new DaggerBattleComponent(this); } } }
Вот что генерирует Dagger 2 за нас для решения проблемы сильных связей (hard dependencies). Если посмотреть на интерфейс, который реализует класс, то вы увидите, что это BattleComponent
— интерфейс, который мы ранее создали и описали в нем метод getWar()
для предоставления экземпляра класса War
.
Эта зависимость предоставляется с использованием шаблона builder
. О данном шаблоне подробнее можно прочитать здесь и здесь.
Изучим кое-что новое
Я надеюсь, что вы четко понимаете, зачем нужен метод getWar()
. Сейчас я хочу добавить ещё пару зависимостей: Starks
и Boltons
. Добавим методы в интерфейс:
@Component interface BattleComponent { fun getWar(): War // добавляем методы fun getStarks(): Starks fun getBoltons(): Boltons }
После внесения изменений заново соберите проект. Теперь проверим класс DaggerBattleComponent
. Если вы всё сделали верно, то увидите примерно следующее:
@Generated( value = "dagger.internal.codegen.ComponentProcessor", comments = "https://google.github.io/dagger" ) public final class DaggerBattleComponent implements BattleComponent { private DaggerBattleComponent(Builder builder) {} public static Builder builder() { return new Builder(); } public static BattleComponent create() { return new Builder().build(); } @Override public War getWar() { return new War(getStarks(), getBoltons()); } @Override public Starks getStarks() { return new Starks(); } @Override public Boltons getBoltons() { return new Boltons(); } public static final class Builder { private Builder() {} public BattleComponent build() { return new DaggerBattleComponent(this); } } }
Как видно, Dagger 2 реализовал методы getStarks()
и getBoltons()
.
Мы указали Dagger 2 получить эти зависимости с помощью аннотации @Inject
в классе Boltons
. Давайте кое-что сломаем. Уберите аннотацию @Inject
из класса Boltons
. Соберите проект заново.
Ничего не произошло? Да, вы не получили никакой ошибки, но попробуйте запустить проект. Вы должны получить следующую ошибку:
Если прочитаете текст ошибки, то он явно говорит о том, что методы getWar()
и getBoltons()
не будут работать, если нет пометок аннотациями @Inject
или @Provides
.
Как ранее упоминалось, Dagger 2 позволяет легко отслеживать ошибки. Можете немного поиграть с этим классом.
Аннотации @Module и @Provides
Копнем глубже и разберемся с парой полезных аннотаций — @Module
и @Provides
. Их стоит использовать, если размер вашего проекта увеличивается.
@Module
Если кратко, то эта аннотация отмечает модули и классы. Поговорим об Android. У нас может быть модуль ContextModule
и этот модуль будет предоставлять зависимости ApplicationContext
и Context
для других классов. Для этого мы должны пометить класс ContextModule
аннотацией Module
.
@Provides
Если кратко, то данная аннотация нужна для пометки методов, которые предоставляют зависимости, внутри модулей. В ранее описанном примере мы пометили класс ContextModule
аннотацией @Module
, но также нам необходимо пометить методы, которые предоставляют зависимости ApplicationContext
и Context
аннотацией @Provides
.
Посмотрите небольшой пример (ссылка на ветку).
Пример
Возьмем два сервиса, предоставляемых Браавосом, — деньги (Cash) и солдаты (Soldiers) (я не уверен, что они предоставляют такие услуги, но рассмотрим это только для примера). Создадим два класса:
class Cash() { }
class Soldiers() { }
Теперь создадим модуль и назовем его BraavosModule
. Он будет снабжать нас двумя зависимостями — Cash
и Soldiers
.
@Module // Модуль class BraavosModule( private val cash: Cash, private val soldiers: Soldiers ) { @Provides // Предоставляет зависимость Cash fun provideCash(): Cash { return cash } @Provides // Предоставляет зависимость Soldiers fun provideSoldiers(): Soldiers { return soldiers } }
Как мы видели ранее, необходимо пометить все модули аннотацией @Module
, а методы, предоставляющие зависимости — аннотацией @Provides
.
Вернемся к классу BattleOfBastards
и укажем компоненту реализовывать методы provideCash()
и provideSoldiers()
.
@Component(modules = [BraavosModule::class]) interface BattleComponent { fun getWar(): Was fun getCash(): Cash fun getSoldiers(): Soldiers }
fun main() { val cash = Cash() val soldiers = Soldiers() val component = DaggerBattleComponent .builder() .braavosModule(BraavosModule(cash, soldiers)) .build() val war = component.getWar() war.prepare() war.report() // используем деньги и солдат component.getCash() component.getSoldiers() }
Обратите внимание на то, что модуль добавлен в объявление аннотации @Component
. Это говорит о том, что компонент будет содержать внутри себя данный модуль:
@Component(modules = [BraavosModule::class])
После всех изменений соберите проект заново. Вы увидите ошибку в методе .create()
класса DaggerBattleComponent
. Она возникла в связи с тем, что при добавлении модуля необходимо передать эту зависимость Dagger 2. Выглядит это так:
val component = DaggerBattleComponent .builder() .braavosModule(BraavosModule(cash, soldiers)) .build()
После включения всех модулей вы можете начать использовать их методы через Component
:
component.getCash() component.getSoldiers()
Если вы хотите убедиться, то наведите курсор на DaggerBattleComponent
и нажмите Ctrl+B (или Ctrl+ЛКМ, Command+ЛКМ). Вы увидите, что модуль BraavosModule
включен в класс для предоставления зависимостейCash
иSoldiers
.
Резюме
Мы проанализировали генерируемые Dagger 2 классы и заметили, что Dagger 2 использует шаблон builder
для предоставления зависимостей. Также рассмотрели простой пример использования аннотаций @Module
и @Provides
.
Что дальше?
В следующей статье мы рассмотрим пример Android приложения с использованием Dagger 2.
Источник: Dagger 2 для начинающих Android разработчиков. Dagger 2. Часть 2