2015-04-09 17 views
24

Ich habe wahrscheinlich etwas verpasst, aber ich dachte Scopes wie @Singleton werden verwendet, um "begrenzte Lebenszyklen" zu definieren.Scopes in Dagger 2

Ich benutze Dolch 2 in einer Android-App (aber ich glaube nicht, dass das Problem überhaupt Android ist).

Ich habe 1 Modul:

@Module public class MailModule { 

    @Singleton @Provides public AccountManager providesAccountManager() { 
    return new AccountManager(); 
    } 

    @Singleton @Provides public MailProvider providesMailProvider(AccountManager accountManager) { 
    return new MailProvider(accountManager); 
    } 
} 

Ich habe zwei verschiedene Komponenten mit @Singleton -umfang:

@Singleton 
@Component(modules = MailModule.class) 
public interface LoginComponent { 

    public LoginPresenter presenter(); 
} 


@Singleton 
@Component(
    modules = MailModule.class 
) 
public interface MenuComponent { 

    MenuPresenter presenter(); 

} 

Sowohl MenuPresenter und LoginPresenter, haben einen @Inject Konstruktor. Während MenuPresenter MailProvider als Parameter erwartet, nimmt LoginPresenter eine AccountManager:

@Inject public MenuPresenter(MailProvider mailProvider) { ... } 

    @Inject public LoginPresenter(AccountManager accountManager) { ... } 

Aber jedes Mal, verwende ich die Komponenten a MenuPresenter oder LoginPresenter ich eine frische neue Instanz von MailProvider und AccountManager erhalten zu schaffen. Ich dachte, sie wären im gleichen Umfang und sollten daher eine Art Singleton sein (im selben Umfang).

Habe ich etwas völlig falsch verstanden. Wie definiere ich einen echten Singleton für mehrere Komponenten in Dolch 2?

Antwort

45

Ich nehme an, dass LoginComponent und MenuComponent separat verwendet werden, z. in LoginActivity und MenuActivity. Jede Komponente ist in Activity.onCreate eingebaut. Wenn dies der Fall ist, werden Komponenten jedes Mal neu erstellt, wenn neue Aktivitäten erstellt werden, Module und Abhängigkeiten auch unabhängig davon, an welchen Bereich sie sich binden. Daher erhalten Sie jedes Mal neue Instanzen von MainProvider und AccountManager.

MenuActivity und LoginActivity haben separate Livezyklen, daher können Abhängigkeiten von MailModule in beiden nicht Singleton sein. Was Sie brauchen, ist Root-Komponente mit @Singleton Geltungsbereich zu deklarieren (z. B. in Anwendung Unterklasse), machen MenuComponent und LoginComponent davon abhängen. Aktivitätslevel-Komponente kann nicht sein @Singleton scoped, besser Sie Ihre eigenen Bereiche erstellen @Scope Anmerkung verwenden, z.B .:

@Retention(RetentionPolicy.RUNTIME) 
@Scope 
public @interface MenuScope { 
} 

Oder Sie können sie unscoped verlassen.

Tive In Bezug auf alle hier kurz von der ersten Dagger 2 proposal:

  • Eine bestimmte Komponente kann nur Bindungen (einschließlich Umfang Anmerkungen:

    @Singleton 
    @Component(modules = {…}) 
    public interface ApplicationComponent {} 
    

    Diese Erklärung Dolch folgende Einschränkungen zu erzwingen ermöglicht auf Klassen), die nicht gekopiert sind oder den angegebenen Umfang haben. I.e. Eine Komponente kann nicht zwei Bereiche darstellen. Wenn kein Bereich aufgeführt ist, dürfen Bindungen nur nicht gekopiert werden.

  • Eine Bereichskomponente kann nur eine Bereichsabhängigkeit aufweisen. Dies ist der Mechanismus, der es erzwingt, dass zwei Komponenten nicht ihre eigene Bereichsbindung deklarieren. Z.B. Zwei Singleton-Komponenten, die jeweils ihren eigenen @Singleton Cache haben, wären defekt.
  • Der Bereich für eine Komponente darf in keiner ihrer transitiven Abhängigkeiten enthalten sein. Beispiel: SessionScoped -> RequestScoped -> SessionScoped macht keinen Sinn und ist ein Fehler.
  • @Singleton wird speziell behandelt, da es keine abhängigen Abhängigkeiten haben kann. Jeder erwartet Singleton als "Wurzel".

Ziel dieser Kombination von Regeln ist, dass zu erzwingen, wenn Umfang angewandt wird, Komponenten mit der gleichen Struktur zusammengesetzt sind, die wir mit Dagger 1,0 plus() haben, verwendet werden "ObjectGraphs d, aber mit der Fähigkeit, zu haben statische Kenntnisse aller Bindungen und ihrer Bereiche. Um es anders zu setzen, wenn Scopes angewendet werden, begrenzt dies die Graphen können nur diejenigen gebaut werden, die korrekt konstruiert werden können.

Aus meiner eigenen Praxis ist es klarer, @Singleton überhaupt nicht zu verwenden. Stattdessen verwende ich @ApplicationScope. Es dient zur Definition von Singletons für die gesamte Anwendung und hat keine zusätzlichen Einschränkungen, wie @Singleton hat.

Hoffe das hilft dir :). Es ist ziemlich schwierig, schnell verstanden zu werden, braucht Zeit, zumindest für mich.

+0

Aber eine Komponente kann nur eine Scope-Annotation haben, oder? Wie würde ich das tun, wenn ich eine Anwendungskomponente mit '@ Application' und LoginComponent mit' @Activity' Scope hätte? – sockeqwe

+1

Rechts. Die Komponente kann nicht mit zwei Bereichen kommentiert werden. Die Aktivitätsumfangskomponente würde alle Abhängigkeiten von der Anwendungsumfangskomponente haben, wenn sie in der Annotation '@Component (Abhängigkeiten = ApplicationComponent.class) 'definiert ist. Stellen Sie sich Komponenten mit Bereichen als Graphen und Untergraphen vor. Anwendungskomponente und ihr Gültigkeitsbereich - Wurzelgraph, Aktivitätskomponente und ihr Gültigkeitsbereich - Untergraph der Wurzel. –

+0

Ermöglicht es die Verwendung von '' @ @ Singleton''' Zielfernrohr, '' '@ Application''' Abhängigkeiten in' '' Activity''' Geltungsbereich zu injizieren? Ein Beispiel wäre, wenn MyPresenter von '' '@ Application''' ist und ich es in MyActivity mit dem' '' @ Activity''-Bereich einspeisen möchte. – AAverin

7

Sie können Folgendes tun, um einen echten Singleton für mehrere Komponenten zu definieren. Ich nehme @ApplicationScoped und @ActivityScoped an, um die verschiedenen Bereiche zu sein.

@Module public class MailModule { 
    @Provides @ApplicationScoped 
    public AccountManager providesAccountManager() { 
    return new AccountManager(); 
    } 

    @Provides @ApplicationScoped 
    public MailProvider providesMailProvider(AccountManager accountManager) { 
     return new MailProvider(accountManager); 
    } 
} 

Dann wird ein MailComponent kann für die MailModule definiert werden. Die LoginComponent und MenuComponent können von der MailComponent abhängen.

@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(); 
} 

The MailComponent kann wie unten gezeigt initialisiert werden und kann in MenuComponent und LoginComponent wieder unten gezeigt ist, verwendet werden.

MailComponent mailComponent = DaggerMailComponent.builder().build(); 

DaggerMenuComponent.builder().mailComponent(mailComponent).build(); 

DaggerLoginComponent.builder().mailComponent(mailComponent).build()    
+1

Gibt es irgendeinen Unterschied zwischen dem Schreiben von '@ApplicationScoped' oben auf jeder Methode von Module und dem einmaligen Schreiben auf @Module? –

+2

@JemshitIskenerov - Die '@ ApplicationScoped' Annotation oben auf' @ Module' wird keine Wirkung haben. Der Zweck eines Moduls besteht in der Bereitstellung von Abhängigkeiten durch Provider-Methoden (Methoden, die mit Annotation "@ Provides" gekennzeichnet sind). Diese Provider-Methoden können Bereiche haben oder nicht. Daher ist es wichtig, Bereiche auf Anbietermethodenebene zu definieren. –