Próbuję objąć głową zakresy w Dagger 2, a konkretnie cykl życia wykresów z zakresem. Jak utworzyć komponent, który zostanie wyczyszczony po opuszczeniu zakresu.
W przypadku aplikacji na Androida, używając Daggera 1.x, generalnie masz zasięg główny na poziomie aplikacji, który rozszerzyłbyś, aby utworzyć zakres podrzędny na poziomie aktywności.
public class MyActivity {
private ObjectGraph mGraph;
public void onCreate() {
mGraph = ((MyApp) getApplicationContext())
.getObjectGraph()
.plus(new ActivityModule())
.inject(this);
}
public void onDestroy() {
mGraph = null;
}
}
Zakres podrzędny istniał tak długo, jak długo zachowałeś do niego odniesienie, co w tym przypadku był cyklem życia Twojej aktywności. Porzucenie referencji w onDestroy zapewniło, że wykres o określonym zakresie może być bezużyteczny.
EDYTOWAĆ
Jesse Wilson niedawno opublikował mea culpa
Dagger 1.0 źle schrzanił nazwy zakresów ... Adnotacja @Singleton jest używana zarówno do wykresów głównych, jak i niestandardowych, więc trudno jest ustalić, jaki jest rzeczywisty zakres rzeczy.
i wszystko inne, co przeczytałem / usłyszałem, wskazuje na Dagger 2 poprawiający sposób działania lunet, ale staram się zrozumieć różnicę. Zgodnie z komentarzem @Kirill Boyarshinov poniżej, cykl życia komponentu lub zależności jest nadal określany, jak zwykle, przez konkretne odniesienia. Czy zatem różnica między celownikami Dagger 1.xi 2.0 jest kwestią jasności semantycznej?
Moje zrozumienie
Sztylet 1.x
Zależności były albo @Singleton
nie. Dotyczyło to również zależności w grafie głównym i podgrafach, co prowadziło do niejednoznaczności co do tego, do którego wykresu zależność została przypisana (zobacz In Dagger są singletonami w pod-grafie zapisanym w pamięci podręcznej, czy też zawsze zostaną odtworzone, gdy nowy podgraf aktywności jest zbudowany? )
Sztylet 2.0
Niestandardowe zakresy umożliwiają tworzenie jasnych semantycznie zakresów, ale są funkcjonalnym odpowiednikiem stosowania @Singleton
w 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;
}
}
Na wynos jest to, że używanie @PerActivity
przekazuje twoje zamiary dotyczące cyklu życia tego komponentu, ale ostatecznie możesz używać komponentu w dowolnym miejscu / czasie. Jedyną obietnicą Daggera jest to, że dla danego komponentu metody z adnotacjami zakresu zwrócą pojedynczą instancję. Zakładam również, że Dagger 2 używa adnotacji zakresu na komponencie, aby sprawdzić, czy moduły zapewniają tylko zależności, które są albo w tym samym zakresie, albo bez zakresu.
W podsumowaniu
Zależności są nadal pojedyncze lub inne, ale @Singleton
są teraz przeznaczone dla pojedynczych wystąpień na poziomie aplikacji, a niestandardowe zakresy są preferowaną metodą opisywania pojedynczych zależności o krótszym cyklu życia.
Deweloper jest odpowiedzialny za zarządzanie cyklem życia komponentów / zależności poprzez usuwanie odwołań, które nie są już potrzebne i odpowiada za zapewnienie, że komponenty są tworzone tylko raz w zakresie, dla którego są przeznaczone, ale niestandardowe adnotacje zakresu ułatwiają identyfikację tego zakresu .
Pytanie za 64 000 $ *
Czy moje rozumienie zakresów i cykli życia Dagger 2 jest prawidłowe?
* Właściwie nie jest to pytanie za 64 000 $.
plus()
odniesienia do nowego wykresu był przechowywany w Aktywności i był powiązany z jego cyklem życia (dereferencjowany wonDestroy
). Jeśli chodzi o zakresy, zapewniają one, że implementacje komponentów są generowane bez błędów w czasie kompilacji, przy spełnieniu każdej zależności. Więc to nie tylko w celach dokumentacyjnych. Zobacz przykład z tego wątku .Odpowiedzi:
Co do twojego pytania
Krótka odpowiedź brzmi: ty to określasz . Twoim komponentom można nadać zakres, taki jak
@Scope @Retention(RetentionPolicy.RUNTIME) public @interface ApplicationScope { } @Scope @Retention(RetentionPolicy.RUNTIME) public @interface ActivityScope { }
Są one przydatne w dwóch przypadkach:
.
@Component(modules={ApplicationModule.class}) @ApplicationScope public interface ApplicationComponent { Something something(); AnotherThing anotherThing(); void inject(Whatever whatever); } @Module public class ApplicationModule { @ApplicationScope //application-scoped provider, only one can exist per component @Provides public Something something() { return new Something(); } @Provides //unscoped, each INJECT call creates a new instance public AnotherThing anotherThing() { return new AnotherThing(); } }
Można to zrobić za pomocą
@Subcomponent
adnotacji lub zależności komponentów. Osobiście wolę zależności.@Component(modules={ApplicationModule.class}) @ApplicationScope public interface ApplicationComponent { Something something(); AnotherThing anotherThing(); void inject(Whatever whatever); ActivityComponent newActivityComponent(ActivityModule activityModule); //subcomponent factory method } @Subcomponent(modules={ActivityModule.class}) @ActivityScope public interface ActivityComponent { ThirdThingy thirdThingy(); void inject(SomeActivity someActivity); } @Module public class ActivityModule { private Activity activity; public ActivityModule(Activity activity) { this.activity = activity; } //... } ApplicationComponent applicationComponent = DaggerApplicationComponent.create(); ActivityComponent activityComponent = applicationComponent.newActivityComponent(new ActivityModule(SomeActivity.this));
Lub możesz użyć takich zależności komponentów
@Component(modules={ApplicationModule.class}) @ApplicationScope public class ApplicationComponent { Something something(); AnotherThing anotherThing(); void inject(Whatever whatever); } @Component(dependencies={ApplicationComponent.class}, modules={ActivityModule.class}) @ActivityScope public interface ActivityComponent extends ApplicationComponent { ThirdThingy thirdThingy(); void inject(SomeActivity someActivity); } @Module public class ActivityModule { private Activity activity; public ActivityModule(Activity activity) { this.activity = activity; } //... } ApplicationComponent applicationComponent = DaggerApplicationComponent.create(); ActivityComponent activityComponent = DaggerActivityComponent.builder().activityModule(new ActivityModule(SomeActivity.this)).build();
Ważne informacje:
Dostawca z zakresem tworzy jedno wystąpienie dla tego danego zakresu dla każdego składnika . Oznacza to, że komponent śledzi swoje własne wystąpienia, ale inne komponenty nie mają wspólnej puli zasięgu ani jakiejś magii. Aby mieć jedną instancję w danym zakresie, potrzebujesz jednej instancji komponentu. Dlatego należy zapewnić
ApplicationComponent
dostęp do jego własnych zależności w zakresie.Komponent może obejmować tylko jeden zakres. Zależności wielu składników o określonym zakresie są niedozwolone.
źródło