web-dev-qa-db-fra.com

Portées dans Dagger 2

J'ai probablement raté quelque chose, mais je pensais que des étendues comme @Singleton sont utilisées pour définir des "cycles de vie étendus".

J'utilise Dagger 2 dans une application Android (mais je ne pense pas que le problème soit Android lié du tout)).

J'ai 1 module:

@Module public class MailModule {

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

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

J'ai deux composants différents avec @Singleton portée:

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

  public LoginPresenter presenter();
}


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

  MenuPresenter presenter();

}

MenuPresenter et LoginPresenter ont un @Inject constructeur. Alors que MenuPresenter attend MailProvider comme paramètre, LoginPresenter prend un AccountManager:

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

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

Mais chaque fois que j'utilise les composants pour créer un MenuPresenter ou LoginPresenter j'obtiens une nouvelle instance de MailProvider et AccountManager. Je pensais qu'ils étaient dans la même portée et devraient donc être une sorte de singleton (dans la même portée).

Ai-je compris quelque chose de complètement faux. Comment définir un vrai singleton pour plusieurs composants dans la dague 2?

26
sockeqwe

Je suppose que LoginComponent et MenuComponent sont utilisés séparément, par exemple dans LoginActivity et MenuActivity. Chaque composant est intégré dans Activity.onCreate. Si tel est le cas, les composants sont recréés chaque fois qu'une nouvelle activité est créée, ainsi que les modules et les dépendances, indépendamment de l'étendue à laquelle ils sont liés. Par conséquent, vous obtenez à chaque fois de nouvelles instances de MainProvider et AccountManager.

MenuActivity et LoginActivity ont des cycles de vie distincts, donc les dépendances de MailModule ne peuvent pas être singleton dans les deux. Ce dont vous avez besoin est de déclarer le composant racine avec @Singleton scope (par exemple dans la sous-classe Application), faites dépendre MenuComponent et LoginComponent. Le composant de niveau d'activité ne peut pas être de portée @Singleton, mieux vaut créer vos propres étendues en utilisant @Scope annotation, par exemple:

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

Ou vous pouvez les laisser sans champ.

En ce qui concerne les portées, voici une brève description initiale proposition Dagger 2 :

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

Cette déclaration permet à dagger d'appliquer les contraintes suivantes:

  • Un composant donné ne peut avoir que des liaisons (y compris des annotations de portée sur les classes) qui sont non étendues ou de la portée déclarée. C'est à dire. un composant ne peut pas représenter deux étendues. Lorsqu'aucune étendue n'est répertoriée, les liaisons peuvent uniquement être non étendues.
  • Un composant de portée ne peut avoir qu'une seule dépendance de portée. Il s'agit du mécanisme qui garantit que deux composants ne déclarent pas chacun leur propre liaison de portée. Par exemple. Deux composants Singleton qui ont chacun leur propre cache @Singleton seraient rompus.
  • La portée d'un composant ne doit apparaître dans aucune de ses dépendances transitives. Par exemple: SessionScoped -> RequestScoped -> SessionScoped n'a aucun sens et est un bug.
  • @Singleton est traité spécialement en ce qu'il ne peut avoir aucune dépendance de portée. Tout le monde s'attend à ce que Singleton soit la "racine".

Le but de cette combinaison de règles est de faire en sorte que lorsque la portée est appliquée, les composants sont composés avec la même structure que nous avions avec Dagger 1.0 plus () 'd ObjectGraphs, mais avec la capacité d'avoir une connaissance statique de tous les fixations et leurs portées. En d'autres termes, lorsque des étendues sont appliquées, cela limite les graphiques qui peuvent être construits à ceux qui peuvent être correctement construits.

D'après ma propre pratique, il est plus clair de ne pas utiliser @Singleton du tout. Au lieu de cela, j'utilise @ApplicationScope. Il sert à définir des singletons sur toute l'application et n'a pas de restrictions supplémentaires comme @Singleton a.

J'espère que cela vous aide :). C'est assez difficile à comprendre rapidement, cela prend du temps, du moins pour moi.

50
Kirill Boyarshinov

Vous pouvez effectuer les opérations suivantes pour définir un véritable singleton pour plusieurs composants. Je suppose @ApplicationScoped et @ActivityScoped pour être les différentes portées.

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

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

Ensuite, un MailComponent peut être défini pour le MailModule. LoginComponent et MenuComponent peuvent dépendre de 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 peut être initialisé comme indiqué ci-dessous et peut être utilisé dans MenuComponent et LoginComponent comme illustré ci-dessous.

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

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

DaggerLoginComponent.builder().mailComponent(mailComponent).build()            
7
Praveer Gupta