J'essaie de comprendre les portées de Dagger 2, en particulier le cycle de vie des graphiques scoped. Comment créer un composant qui sera nettoyé lorsque vous quitterez la portée.
Dans le cas d'une application Android, en utilisant Dagger 1.x, vous avez généralement une étendue racine au niveau de l'application que vous étendriez pour créer une étendue enfant au niveau de l'activité.
public class MyActivity {
private ObjectGraph mGraph;
public void onCreate() {
mGraph = ((MyApp) getApplicationContext())
.getObjectGraph()
.plus(new ActivityModule())
.inject(this);
}
public void onDestroy() {
mGraph = null;
}
}
L'étendue enfant existait tant que vous en gardiez une référence, ce qui dans ce cas était le cycle de vie de votre activité. La suppression de la référence dans onDestroy a garanti que le graphe de portée était libre d'être récupéré.
ÉDITER
Jesse Wilson a récemment publié un mea culpa
Dagger 1.0 a mal vissé ses noms de portée ... L'annotation @Singleton est utilisée à la fois pour les graphiques racine et les graphiques personnalisés, il est donc difficile de déterminer quelle est la portée réelle d'une chose.
et tout ce que j'ai lu / entendu indique que Dagger 2 améliore le fonctionnement des oscilloscopes, mais j'ai du mal à comprendre la différence. Selon le commentaire de @Kirill Boyarshinov ci-dessous, le cycle de vie d'un composant ou d'une dépendance est toujours déterminé, comme d'habitude, par des références concrètes. La différence entre les portées Dagger 1.x et 2.0 est-elle purement une question de clarté sémantique?
Ma compréhension
Dague 1.x
Les dépendances étaient soit @Singleton
ou non. Cela était également vrai pour les dépendances dans le graphe racine et les sous-graphes, conduisant à une ambiguïté quant au graphe auquel la dépendance était liée (voir Dans Dagger sont des singletons dans le sous-graphe mis en cache ou seront-ils toujours recréés lorsqu'un nouveau sous-graphe d'activité est construit? )
Dague 2.0
Les portées personnalisées vous permettent de créer des portées sémantiquement claires, mais sont fonctionnellement équivalentes à l'application @Singleton
dans Dagger 1.x.
// Application level
@Singleton
@Component( modules = MyAppModule.class )
public interface MyAppComponent {
void inject(Application app);
}
@Module
public class MyAppModule {
@Singleton @Named("SingletonScope") @Provides
StringBuilder provideStringBuilderSingletonScope() {
return new StringBuilder("App");
}
}
// Our custom scope
@Scope public @interface PerActivity {}
// Activity level
@PerActivty
@Component(
dependencies = MyAppComponent.class,
modules = MyActivityModule.class
)
public interface MyActivityComponent {
void inject(Activity activity);
}
@Module
public class MyActivityModule {
@PerActivity @Named("ActivityScope") @Provides
StringBuilder provideStringBuilderActivityScope() {
return new StringBuilder("Activity");
}
@Name("Unscoped") @Provides
StringBuilder provideStringBuilderUnscoped() {
return new StringBuilder("Unscoped");
}
}
// Finally, a sample Activity which gets injected
public class MyActivity {
private MyActivityComponent component;
@Inject @Named("AppScope")
StringBuilder appScope
@Inject @Named("ActivityScope")
StringBuilder activityScope1
@Inject @Named("ActivityScope")
StringBuilder activityScope2
@Inject @Named("Unscoped")
StringBuilder unscoped1
@Inject @Named("Unscoped")
StringBuilder unscoped2
public void onCreate() {
component = Dagger_MyActivityComponent.builder()
.myApplicationComponent(App.getComponent())
.build()
.inject(this);
appScope.append(" > Activity")
appScope.build() // output matches "App (> Activity)+"
activityScope1.append("123")
activityScope1.build() // output: "Activity123"
activityScope2.append("456")
activityScope1.build() // output: "Activity123456"
unscoped1.append("123")
unscoped1.build() // output: "Unscoped123"
unscoped2.append("456")
unscoped2.build() // output: "Unscoped456"
}
public void onDestroy() {
component = null;
}
}
Ce qu'il faut retenir, c'est que l'utilisation @PerActivity
communique votre intention concernant le cycle de vie de ce composant, mais en fin de compte, vous pouvez utiliser le composant n'importe où / n'importe quand. La seule promesse de Dagger est que, pour un composant donné, les méthodes annotées de portée renverront une seule instance. Je suppose également que Dagger 2 utilise l'annotation de portée sur le composant pour vérifier que les modules ne fournissent que des dépendances qui sont dans la même portée ou non.
En résumé
Les dépendances sont toujours singleton ou non singleton, mais elles @Singleton
sont désormais destinées aux instances de singleton au niveau de l'application et les étendues personnalisées sont la méthode préférée pour annoter les dépendances de singleton avec un cycle de vie plus court.
Le développeur est responsable de la gestion du cycle de vie des composants / dépendances en supprimant les références qui ne sont plus nécessaires et de s'assurer que les composants ne sont créés qu'une seule fois dans la portée pour laquelle ils sont destinés, mais les annotations de portée personnalisées facilitent l'identification de cette portée. .
La question à 64 000 $ *
Ma compréhension des portées et des cycles de vie de Dagger 2 est-elle correcte?
* Pas vraiment une question à 64 000 $.
plus()
référence à un nouveau graphique était stocké dans Activity et était lié à son cycle de vie (déréférencé dansonDestroy
). Quant aux étendues, elles garantissent que vos implémentations de composants sont générées sans erreur au moment de la compilation, avec chaque dépendance satisfaite. Ce n'est donc pas seulement à des fins de documentation. Découvrez un exemple de ce fil .