Sto cercando di avvolgere la mia testa attorno agli ambiti in Dagger 2, in particolare il ciclo di vita dei grafici con ambito. Come si crea un componente che verrà ripulito quando si esce dall'ambito.
Nel caso di un'applicazione Android, usando Dagger 1.x si ha generalmente un ambito radice a livello di applicazione che si estenderebbe per creare un ambito figlio a livello di attività.
public class MyActivity {
private ObjectGraph mGraph;
public void onCreate() {
mGraph = ((MyApp) getApplicationContext())
.getObjectGraph()
.plus(new ActivityModule())
.inject(this);
}
public void onDestroy() {
mGraph = null;
}
}
L'ambito figlio esisteva fintanto che hai mantenuto un riferimento ad esso, che in questo caso era il ciclo di vita della tua attività. Eliminare il riferimento in onDestroy ha assicurato che il grafico con ambito fosse libero di essere raccolto.
MODIFICARE
Jesse Wilson ha recentemente pubblicato un mea culpa
Dagger 1.0 ha rovinato gravemente i nomi dei suoi scopi ... L'annotazione @Singleton è usata sia per i grafici di root che per quelli personalizzati, quindi è difficile capire quale sia l'ambito reale di una cosa.
e tutto il resto che ho letto / sentito punti su Dagger 2 che migliora il modo in cui funzionano gli oscilloscopi, ma faccio fatica a capire la differenza. Secondo il commento di @Kirill Boyarshinov di seguito, il ciclo di vita di un componente o di una dipendenza è ancora determinato, come al solito, da riferimenti concreti. Quindi la differenza tra gli ambiti Dagger 1.xe 2.0 è puramente una questione di chiarezza semantica?
La mia comprensione
Pugnale 1.x
Le dipendenze erano @Singleton
o no. Ciò era altrettanto vero per le dipendenze nel grafico principale e nei sottografi, il che porta all'ambiguità su quale grafico era legato alla dipendenza (vedi Dagger sono i Singleton all'interno del sotto-grafico memorizzati nella cache o saranno sempre ricreati quando un nuovo sotto-grafico di attività è costruito? )
Pugnale 2.0
Gli ambiti personalizzati consentono di creare ambiti semanticamente chiari, ma sono funzionalmente equivalenti all'applicazione @Singleton
in 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;
}
}
L'asporto è che l'uso @PerActivity
comunica la tua intenzione riguardo al ciclo di vita di questo componente, ma alla fine puoi utilizzare il componente ovunque / in qualsiasi momento. L'unica promessa di Dagger è che, per un dato componente, i metodi annotati dell'ambito restituiranno una singola istanza. Suppongo anche che Dagger 2 utilizzi l'annotazione dell'ambito sul componente per verificare che i moduli forniscano solo dipendenze che sono nello stesso ambito o non nell'ambito.
In sintesi
Le dipendenze sono ancora singole o non singole, ma @Singleton
ora sono intese per istanze singleton a livello di applicazione e gli ambiti personalizzati sono il metodo preferito per annotare le dipendenze singleton con un ciclo di vita più breve.
Lo sviluppatore è responsabile della gestione del ciclo di vita dei componenti / dipendenze eliminando i riferimenti non più necessari e la responsabilità di garantire che i componenti vengano creati una sola volta nell'ambito nell'ambito a cui sono destinati, ma le annotazioni dell'ambito personalizzato semplificano l'identificazione di tale ambito .
La domanda da $ 64k *
La mia comprensione degli ambiti e dei cicli di vita di Dagger 2 è corretta?
* In realtà non è una domanda da $ 64'000.
plus()
riferimento a un nuovo grafico è stato memorizzato in Activity ed è stato associato al suo ciclo di vita (senza riferimentionDestroy
). Per quanto riguarda gli ambiti, assicurano che le implementazioni dei componenti vengano generate senza errori in fase di compilazione, con ogni dipendenza soddisfatta. Quindi non solo a scopo di documentazione. Guarda alcuni esempi da questa discussione .