Создание всех модулей для DI в классе приложений для Android - PullRequest
0 голосов
/ 09 января 2019

Одна из вещей, которые я заметил, что многие разработчики делают, это создание класса, который наследуется от Application, а затем создание компонента путем внедрения зависимостей, включающего практически все модули, составляющие их приложение. Это делается в методе onCreate. Я нахожу это довольно странным. Почему вы хотите внедрить каждый модуль в класс Application и сделать его доступным для всех? В конце концов, большинство модулей, таких как докладчики, привязаны к одному действию и никогда не будут использоваться для какого-либо другого действия. Так почему бы вам не просто создать компонент в упражнении и включить в него только те модули, которые вам нужны, а в случае занятия - это класс докладчика.

1 Ответ

0 голосов
/ 11 января 2019

Я не уверен, что согласен с предпосылкой: большинство приложений создают компонент в Application # onCreate, но я считаю, что большинство приложений также имеют отдельные компоненты, которые содержат привязки для активности, фрагмента или службы, и эти компоненты / модули существуют только и загружаются в класс только при использовании конкретной активности / фрагмента / службы, о которой идет речь.

Область применения и жизненный цикл

Dagger управляет жизненным циклом объекта ( "scope" ) через отдельные компоненты, каждый из которых может иметь свой собственный набор модулей. Вы аннотируете свой компонент одной или несколькими аннотациями области, а затем любые привязки, которые вы аннотируете с той же областью действия (или любые классы с этой аннотацией области и конструкторами, аннотированными @Inject), будут созданы ровно один раз и сохранены в компоненте. Это противоречит поведению Даггера по умолчанию, которое заключается в вызове метода @Provides или создании нового экземпляра объекта для каждого вызова метода компонента или каждого аннотированного @Inject поля. Вы контролируете, когда создаете экземпляр компонента, так что вы можете контролировать семантику своей области: если вы должны создать аннотацию области с именем @PerActivity, и вы создадите новый экземпляр компонента для каждого экземпляра Activity, который создает Android, то вы можете быть уверены, что любые привязки, помеченные @PerActivity, будут возвращать один и тот же экземпляр в течение всего срока действия этого действия. Аналогично, вы можете создать @UserScope, где каждый пользователь получает отдельный экземпляр компонента. Единственная стандартизированная область действия в JSR-330 - @Singleton, которая должна применяться ко всему приложению.

Однако что, если вы хотите смешать области, например, иметь @PerActivity StatusBarPresenter в зависимости от @Singleton LoginService? Dagger требует, чтобы вы хранили их в двух отдельных компонентах, так что StatusBarPresenter может быть определен в @PerActivity ActivityComponent, а LoginService может быть определен в @Singleton ApplicationComponent. Вам необходимо установить связь между этим ActivityComponent и ApplicationComponent, что можно сделать с помощью компонентов с зависимостями или субкомпонентов .

Компоненты с зависимостями

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

@Singleton @Component(modules = {FooModule.class, BarModule.class})
interface ApplicationComponent { 
  Foo foo();
  // Bar also exists, but is not listed. Let's say Foo uses it internally.
}

@PerActivity @Component(
     modules = {BazModule.class},
     dependencies = {ApplicationComponent.class})
interface ActivityComponent {
  Baz baz();
}

ActivityComponent activityComponent =
    DaggerActivityComponent.builder()
        .applicationComponent(yourExistingApplicationComponent)
        .build();

ActivityComponent получает свой собственный шаг генерации кода и может компилироваться параллельно с ApplicationComponent; однако ActivityComponent может получить доступ только к зависимости Foo, но не к Bar. Это потому, что ActivityComponent имеет ApplicationComponent, указанный как зависимость, а ApplicationComponent не отображает Bar. Таким образом, Baz, определенный в ActivityComponent, может автоматически внедрить Foo, но не может внедрить Bar. Фактически, если Foo прекратил использовать Bar, ApplicationComponent может даже не генерировать код для создания Bar вообще.

Подкомпоненты

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

@Singleton @Component(modules = {FooModule.class, BarModule.class})
interface ApplicationComponent { 
  Foo foo();
  // This is a subcomponent builder method, which can also return a
  // @Subcomponent.Builder. More modern code uses the "subcomponents" attribute
  // on the @Module annotation.
  ActivityComponent createActivityComponent();
}

@PerActivity @Subcomponent(
     modules = {BazModule.class},
     dependencies = {ApplicationComponent.class})
interface ActivityComponent {
  Baz baz();
}

ActivityComponent activityComponent =
    yourExistingApplicationComponent.createActivityComponent();
// or, from somewhere that ApplicationComponent injects:
@Inject Provider<ActivityComponent> activityComponentProvider;
ActivityComponent activityComponent = activityComponentProvider.get();

Для подкомпонентов реализация ActivityComponent генерируется одновременно с ApplicationComponent, что также означает, что ApplicationComponent может оценивать потребности ActivityComponent при генерации кода. Следовательно, код для создания экземпляров Bar будет включен в ApplicationComponent, если его использует либо ApplicationComponent, либо ActivityComponent. Однако этот этап сборки может стать медленным, поскольку Dagger потребуется проанализировать график зависимостей всего вашего приложения.

Приложение # OnCreate

Все это возвращается к Application # onCreate и к тому, что вы видите:

  • Если у вас есть синглеты в области приложения, вам, вероятно, нужно получить их из приложения (хотя технически вы также можете использовать статическое поле).
  • Если у вас есть привязки, которые используются в вашем приложении, даже если они не имеют области видимости, вы можете установить их в ApplicationComponent, чтобы вам не приходилось повторять одну и ту же привязку в каждом компоненте.
  • Если вы используете подкомпоненты, то весь процесс генерации кода для всего приложения генерируется за один шаг на уровне приложения, даже если код генерируется как отдельные классы, загружаемые в разное время. Поскольку компонент приложения действует как фабрика, дополнительные классы скрыты, поскольку ваша единственная ссылка на сгенерированное имя класса может относиться к экземпляру ApplicationComponent («DaggerApplicationComponent»).
    • Это может привести к более плавному взаимодействию с разработчиками, поскольку вам не нужно беспокоиться о перечислении зависимости в интерфейсе ApplicationComponent, если вы хотите получить к ней доступ через ActivityComponent.
    • Это также хорошо для Android, потому что в случае подкомпонента Dagger имеет больше информации о том, какие привязки требуются, поэтому он может иногда создавать более компактный код, чем компоненты с зависимостями.
    • Если вы используете dagger.android и @ContributesAndroidInjector, вы используете подкомпоненты с некоторым синтаксическим сахаром сверху. Обратите внимание, что @ContributesAndroidInjector может быть аннотирован аннотациями области и может принимать список modules, который будет передан генерируемому подкомпоненту. Ваш вызов AndroidInjection.inject(this) создаст один из этих экземпляров подкомпонента, загружая при необходимости подкомпонент и его модули.

Таким образом, даже если у вас могут быть очень специфические компоненты с разными жизненными циклами, может показаться, что вся ваша конфигурация Dagger происходит в ApplicationComponent и Application # onCreate и больше нигде.

...