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?
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.
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()