2015-04-09 16 views
24

Probabilmente mi sono perso qualcosa, ma pensavo che Scopes come @Singleton venissero usati per definire "cicli di vita circoscritti".Scopes in Dagger 2

Uso Dagger 2 in un'app Android (ma non penso che il problema riguardi Android).

Ho 1 Modulo:

@Module public class MailModule { 

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

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

Ho due componenti differenti con @Singleton portata:

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

    public LoginPresenter presenter(); 
} 


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

    MenuPresenter presenter(); 

} 

Sia, MenuPresenter e LoginPresenter, hanno un costruttore @Inject. Mentre si aspetta MenuPresenter MailProvider come parametro, LoginPresenter prende un AccountManager:

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

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

Ma ogni volta che utilizzare i componenti per creare un MenuPresenter o LoginPresenter ottengo una nuova istanza fresco di MailProvider e AccountManager. Pensavo che fossero nello stesso scopo e dovessero quindi essere una specie di singleton (nello stesso scopo).

Ho capito qualcosa di completamente sbagliato. Come posso definire un vero singleton per più componenti in Dagger 2?

risposta

45

Suppongo che LoginComponent e MenuComponent vengano utilizzati separatamente, ad es. in LoginActivity e MenuActivity. Ogni componente è costruito in Activity.onCreate. In tal caso, i componenti vengono ricreati ogni volta che vengono create nuove attività, moduli e dipendenze, indipendentemente dalla portata a cui si collegano. Pertanto, si ottengono nuove istanze di MainProvider e AccountManager ogni volta.

MenuActivity e LoginActivity hanno livecycles separati, in modo da dipendenze MailModule non possono essere singleton in entrambi. Quello di cui hai bisogno è dichiarare il componente root con l'ambito @Singleton (ad esempio nella sottoclasse dell'applicazione), fare in modo che MenuComponent e LoginComponent dipendano da questo. componente livello di attività non può essere @Singleton ambito, migliore di creare i propri ambiti di utilizzo @Scope annotazione, ad es .:

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

oppure è possibile lasciare senza ambito.

Per quanto riguarda gli ambiti a tutti ecco breve da iniziale Dagger 2 proposal:

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

Tale dichiarazione consente pugnale per far rispettare i seguenti vincoli:

  • un dato componente può avere solo attacchi (comprese le annotazioni scope sulle classi) che sono senza ambito o dell'ambito dichiarato. I.e. un componente non può rappresentare due ambiti. Quando nessun ambito è elencato, , i binding possono essere annullati.
  • Un componente con ambito può avere solo una dipendenza con ambito. Questo è il meccanismo che impone che due componenti non dichiarino ciascuno il proprio binding con scope . Per esempio. Due componenti Singleton che hanno ciascuno propria cache @Singleton si rompono.
  • L'ambito per un componente non deve apparire in nessuna delle sue dipendenze transitive. Ad es .: SessionScoped -> RequestScoped -> SessionScoped non ha alcun senso ed è un bug.
  • @Singleton viene trattato in modo specifico in quanto non può avere dipendenze di ambito. Tutti si aspettano che Singleton sia la "radice".

L'obiettivo di questa combinazione di regole è per imporre che quando la portata viene applicata, i componenti sono composti con la stessa struttura che abbiamo usato per avere con Dagger 1.0 più() 'd ObjectGraphs, ma con la possibilità per avere una conoscenza statica di tutti i binding e dei loro ambiti. Per mettere in un altro modo, quando vengono applicati gli ambiti, questo limita i grafici di quello può essere costruito solo per quelli che possono essere costruiti correttamente.

Dal mio studio, è più chiaro non utilizzare @Singleton affatto. Invece di quello, io uso @ApplicationScope. Serve a definire singleton su tutta l'applicazione e non ha ulteriori restrizioni come ha @Singleton.

La speranza che ti aiuta :). È abbastanza difficile essere capito velocemente, richiede tempo, almeno per me.

+0

Ma un componente può avere solo un'annotazione Scope, giusto? Come dovrei farlo se ho un componente dell'applicazione con '@ Application' e LoginComponent con l'ambito' @ Activity'? – sockeqwe

+1

Giusto. Il componente non può essere annotato con due ambiti. Il componente dell'ambito di attività avrebbe tutte le dipendenze dal componente dell'ambito dell'applicazione se è definito nell'annotazione '@Component (dependencies = ApplicationComponent.class)'. Pensa a componenti con ambiti come grafici e sottografi. Componente dell'applicazione e relativo ambito: grafico root, componente attività e relativo ambito: sottografo di root. –

+0

Evitare l'uso dell'ambito '' '@ Singleton''' permette di inserire le dipendenze' '' @ Application''' in '' '@ Activity''' scope? L'esempio sarebbe, se MyPresenter è di scope '' '@ Application''' e voglio iniettarlo in MyActivity che è di scope' '' @ Activity'''. – AAverin

7

È possibile effettuare le seguenti operazioni per definire un singleton reale per più componenti. Sto assumendo che @ApplicationScoped e @ActivityScoped siano i diversi ambiti.

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

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

Poi un MailComponent può essere definita per il MailModule. LoginComponent e MenuComponent possono dipendere dallo MailComponent.

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

Il MailComponent può essere inizializzato come mostrato di seguito e può essere utilizzato in MenuComponent e LoginComponent nuovamente mostrate sotto.

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

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

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

C'è qualche differenza tra la scrittura di '@ApplicationScoped' su ciascun metodo del modulo separatamente e la scrittura sopra @Module una volta? –

+2

@JemshitIskenderov - L'inserimento dell'annotazione '@ ApplicationScoped' su' @ Module' non avrà alcun effetto. Lo scopo di un modulo è di fornire dipendenze attraverso metodi di provider (metodi annotati con annotazione '@ Fornisce'). Questi metodi di provider possono avere o meno ambiti. Diventa quindi importante definire gli ambiti a livello di metodo del provider. –

Problemi correlati