Области применения кинжала 2

Я, вероятно, что-то пропустил, но я думал, что Scopes, например @Singleton, используется для определения "скопированных жизненных циклов".

Я использую Dagger 2 в приложении для Android (но я не думаю, что проблема связана с андроидом вообще).

У меня есть 1 модуль:

@Module public class MailModule {

  @Singleton @Provides public AccountManager providesAccountManager() {
    return new AccountManager();
  }

  @Singleton @Provides public MailProvider providesMailProvider(AccountManager accountManager) {
    return new MailProvider(accountManager);
  }
}

У меня есть два разных компонента с областью @Singleton:

@Singleton
@Component(modules = MailModule.class)
public interface LoginComponent {

  public LoginPresenter presenter();
}


@Singleton
@Component(
    modules = MailModule.class
)
public interface MenuComponent {

  MenuPresenter presenter();

}

Оба, MenuPresenter и LoginPresenter, имеют конструктор @Inject. В то время как MenuPresenter ожидает MailProvider в качестве параметра, LoginPresenter принимает AccountManager:

  @Inject public MenuPresenter(MailProvider mailProvider) { ... }

  @Inject public LoginPresenter(AccountManager accountManager) { ... }

Но каждый раз, когда я использую компоненты для создания MenuPresenter или LoginPresenter, я получаю новый новый экземпляр MailProvider и AccountManager. Я думал, что они находятся в одном и том же объеме и поэтому должны быть одноэлементными (в том же объеме).

Я понял что-то совершенно неправильное. Как определить реальный синглтон для нескольких компонентов в кинжале 2?

Ответ 1

Я предполагаю, что LoginComponent и MenuComponent используются отдельно, например. в LoginActivity и MenuActivity. Каждый компонент построен в Activity.onCreate. Если это так, компоненты воссоздаются каждый раз, когда создается новая активность, модули и зависимости, независимо от того, с какой областью они связаны. Поэтому каждый раз вы получаете новые экземпляры MainProvider и AccountManager.

MenuActivity и LoginActivity имеют отдельные livecycles, поэтому зависимости от MailModule не могут быть одноточечными в обоих из них. Вам нужно объявить корневой компонент с областью @Singleton (например, в подклассе Application), заставьте MenuComponent и LoginComponent зависеть от него. Компонент уровня активности не может быть @Singleton, лучше создавать собственные области с помощью аннотации @Scope, например:

@Retention(RetentionPolicy.RUNTIME)
@Scope
public @interface MenuScope {
}

Или вы можете оставить их без доступа.

Что касается областей вообще, кратко из первоначального предложения Кинжал 2:

@Singleton
@Component(modules = {…})
public interface ApplicationComponent {}

Это объявление позволяет кинжалу применять следующие ограничения:

  • У данного компонента могут быть только привязки (включая аннотации объектов к классам), которые не облажаны или объявлены. То есть компонент не может представлять две области. Если перечисленные, привязки могут быть только незарегистрированными.
  • Компонент с областью может иметь только одну зависимую область. Это механизм, который обеспечивает, чтобы два компонента не декларировали каждый собственная привязка к области. Например. Два компонента Singleton, каждый из которых имеет их собственный кеш @Singleton будет сломан.
  • Объем компонента не должен появляться ни в одной из его транзитивных зависимостей. Например: SessionScoped → RequestScoped → SessionScoped не имеет никакого смысла и является ошибкой.
  • @Singleton обрабатывается специально в том смысле, что он не может иметь никаких зависимых областей. Каждый ожидает, что Singleton станет "корнем".

Цель этой комбинации правил состоит в том, чтобы обеспечить соблюдение применяемые компоненты составлены с той же структурой, что мы использовали иметь с Dagger 1.0 plus() d ObjectGraphs, но с возможностью имеют статические знания всех привязок и их областей. Класть это по-другому, когда применяются области применения, это ограничивает графики, чем могут быть построены только для тех, которые могут быть правильно построены.

Из моей собственной практики яснее не использовать @Singleton вообще. Вместо этого я использую @ApplicationScope. Он служит для определения одиночных чисел для всего приложения и не имеет дополнительных ограничений, поскольку @Singleton имеет.

Надеюсь, что это поможет вам:). Это довольно сложно понять быстро, требуется время, для меня, по крайней мере, это было.

Ответ 2

Вы можете сделать следующее, чтобы определить реальный синглтон для нескольких компонентов. Я предполагаю, что @ApplicationScoped и @ActivityScoped будут разными областями.

@Module public class MailModule {
  @Provides @ApplicationScoped 
  public AccountManager providesAccountManager() {
    return new AccountManager();
  }

  @Provides @ApplicationScoped
  public MailProvider providesMailProvider(AccountManager accountManager) {
        return new MailProvider(accountManager);
  }
}

Тогда a MailComponent может быть определено для MailModule. LoginComponent и MenuComponent могут зависеть от MailComponent.

@ApplicationScoped
@Component(modules = MailModule.class)
public interface MailComponent {
  MailProvider mailProvider();
  AccountManager accountManager();
}

@ActivityScoped
@Component(dependencies = MailComponent.class)
public interface LoginComponent {
  LoginPresenter presenter();
}

@ActivityScoped
@Component(dependencies = MailComponent.class)
public interface MenuComponent {
  MenuPresenter presenter();
}

MailComponent может быть инициализирован, как показано ниже, и может использоваться в MenuComponent и LoginComponent, как показано ниже.

MailComponent mailComponent = DaggerMailComponent.builder().build();

DaggerMenuComponent.builder().mailComponent(mailComponent).build();

DaggerLoginComponent.builder().mailComponent(mailComponent).build()