Estoy tratando de comprender los ámbitos en Dagger 2, específicamente el ciclo de vida de los gráficos de ámbito. ¿Cómo se crea un componente que se limpiará cuando abandone el ámbito?
En el caso de una aplicación de Android, al usar Dagger 1.x generalmente tiene un alcance raíz en el nivel de la aplicación que extendería para crear un alcance secundario en el nivel de actividad.
public class MyActivity {
private ObjectGraph mGraph;
public void onCreate() {
mGraph = ((MyApp) getApplicationContext())
.getObjectGraph()
.plus(new ActivityModule())
.inject(this);
}
public void onDestroy() {
mGraph = null;
}
}
El ámbito secundario existió siempre que mantuviera una referencia a él, que en este caso fue el ciclo de vida de su Actividad. Dejar caer la referencia en onDestroy aseguró que el gráfico de alcance fuera libre para ser recolectado.
EDITAR
Jesse Wilson recientemente publicó un mea culpa
Dagger 1.0 jodió mal sus nombres de alcance ... La anotación @Singleton se usa tanto para gráficos raíz como para gráficos personalizados, por lo que es difícil descubrir cuál es el alcance real de una cosa.
y todo lo demás que he leído / escuchado apunta a Dagger 2 mejorando la forma en que funcionan los ámbitos, pero me cuesta entender la diferencia. Según el comentario de @Kirill Boyarshinov a continuación, el ciclo de vida de un componente o dependencia todavía está determinado, como de costumbre, por referencias concretas. Entonces, ¿la diferencia entre los alcances Dagger 1.xy 2.0 es puramente una cuestión de claridad semántica?
Mi entendimiento
Daga 1.x
Las dependencias eran @Singleton
o no. Esto fue igualmente cierto para las dependencias en el gráfico raíz y los subgrafos, lo que lleva a la ambigüedad sobre a qué gráfico estaba vinculada la dependencia (ver En Dagger son Singletons dentro del sub-gráfico en caché o siempre se recrearán cuando una nueva sub-gráfica de actividad está construido? )
Daga 2.0
Los ámbitos personalizados le permiten crear ámbitos semánticamente claros, pero son funcionalmente equivalentes a la aplicación @Singleton
en 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;
}
}
La conclusión es que el uso @PerActivity
comunica su intención con respecto al ciclo de vida de este componente, pero en última instancia, puede usar el componente en cualquier lugar / en cualquier momento. La única promesa de Dagger es que, para un componente dado, los métodos anotados en el alcance devolverán una sola instancia. También supongo que Dagger 2 usa la anotación de alcance en el componente para verificar que los módulos solo proporcionen dependencias que estén en el mismo alcance o sin alcance.
En resumen
Las dependencias siguen siendo singleton o no singleton, pero @Singleton
ahora están destinadas a instancias singleton a nivel de aplicación y los ámbitos personalizados son el método preferido para anotar dependencias singleton con un ciclo de vida más corto.
El desarrollador es responsable de administrar el ciclo de vida de los componentes / dependencias eliminando referencias que ya no son necesarias y responsable de garantizar que los componentes solo se creen una vez en el alcance para el que están destinados, pero las anotaciones de alcance personalizadas hacen que sea más fácil identificar ese alcance .
La pregunta de $ 64k *
¿Es correcto mi comprensión de los ámbitos y ciclos de vida de Dagger 2?
* No es realmente una pregunta de $ 64'000.
plus()
referencia a un nuevo gráfico se almacenaba en Activity y estaba vinculado a su ciclo de vida (desreferenciadoonDestroy
). En cuanto a los ámbitos, aseguran que las implementaciones de sus componentes se generen sin errores en el momento de la compilación, con cada dependencia satisfecha. Por lo tanto, no solo con fines de documentación. Mira algunos ejemplos de este hilo .