java - medium - Alcances en Daga 2
dagger compiler android (2)
Probablemente me perdí algo, pero pensé que Scopes como @Singleton se utilizan para definir "ciclos de vida de ámbito".
Uso Dagger 2 en una aplicación de Android (pero no creo que el problema esté relacionado con Android en absoluto).
Tengo 1 módulo:
@Module public class MailModule {
@Singleton @Provides public AccountManager providesAccountManager() {
return new AccountManager();
}
@Singleton @Provides public MailProvider providesMailProvider(AccountManager accountManager) {
return new MailProvider(accountManager);
}
}
Tengo dos componentes diferentes con el alcance @Singleton
:
@Singleton
@Component(modules = MailModule.class)
public interface LoginComponent {
public LoginPresenter presenter();
}
@Singleton
@Component(
modules = MailModule.class
)
public interface MenuComponent {
MenuPresenter presenter();
}
Ambos, MenuPresenter
y LoginPresenter
, tienen un constructor @Inject
. Mientras MenuPresenter espera MailProvider
como parámetro, LoginPresenter toma un AccountManager
:
@Inject public MenuPresenter(MailProvider mailProvider) { ... }
@Inject public LoginPresenter(AccountManager accountManager) { ... }
Pero cada vez que uso los componentes para crear un MenuPresenter
o LoginPresenter
obtengo una nueva instancia de MailProvider
y AccountManager
. Pensé que estaban en el mismo ámbito y, por lo tanto, deberían ser una especie de singleton (en el mismo ámbito).
¿Entendí algo completamente mal? ¿Cómo defino un singleton real para múltiples componentes en la daga 2?
Puede hacer lo siguiente para definir un singleton real para múltiples componentes. Estoy asumiendo que @ApplicationScoped
y @ActivityScoped
son los diferentes ámbitos.
@Module public class MailModule {
@Provides @ApplicationScoped
public AccountManager providesAccountManager() {
return new AccountManager();
}
@Provides @ApplicationScoped
public MailProvider providesMailProvider(AccountManager accountManager) {
return new MailProvider(accountManager);
}
}
Luego se puede definir un MailComponent
para el MailModule
. LoginComponent
y MenuComponent
pueden depender de 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();
}
El MailComponent
se puede inicializar como se muestra a continuación y se puede usar en MenuComponent
y LoginComponent
nuevamente mostrado a continuación.
MailComponent mailComponent = DaggerMailComponent.builder().build();
DaggerMenuComponent.builder().mailComponent(mailComponent).build();
DaggerLoginComponent.builder().mailComponent(mailComponent).build()
Supongo que LoginComponent
y MenuComponent
se usan por separado, por ejemplo, en LoginActivity
y MenuActivity
. Cada componente está construido en Activity.onCreate
. Si es así, los componentes se recrean cada vez que se crean nuevas actividades, módulos y dependencias, independientemente del alcance al que se unan. Por lo tanto, obtiene nuevas instancias de MainProvider
y AccountManager
cada vez.
MenuActivity
y LoginActivity
tienen LoginActivity
separados, por lo que las dependencias de MailModule
no pueden ser singleton en ambos. Lo que necesita es declarar el componente raíz con el alcance @Singleton
(por ejemplo, en la subclase de la aplicación), hacer que MenuComponent
y LoginComponent
dependan de ello. El componente de nivel de actividad no puede tener un ámbito @Singleton, es mejor crear sus propios ámbitos con la anotación @Scope
, por ejemplo:
@Retention(RetentionPolicy.RUNTIME)
@Scope
public @interface MenuScope {
}
O puedes dejarlos sin cobertura.
En cuanto a los ámbitos, aquí hay un resumen de la propuesta inicial de Dagger 2 :
@Singleton @Component(modules = {…}) public interface ApplicationComponent {}
Esa declaración permite a Dagger imponer las siguientes restricciones:
- Un componente dado solo puede tener enlaces (incluidas las anotaciones de alcance en las clases) que no tienen ámbito o del alcance declarado. Es decir, un componente no puede representar dos ámbitos. Cuando no se enumera ningún ámbito, los enlaces solo pueden estar sin ámbito.
- Un componente de ámbito solo puede tener una dependencia de ámbito. Este es el mecanismo que impone que dos componentes no declaren cada uno su propio enlace de ámbito. Por ejemplo, dos componentes Singleton que tienen cada uno su propio @Singleton Cache se romperían.
- El alcance de un componente no debe aparecer en ninguna de sus dependencias transitivas. Por ejemplo: SessionScoped -> RequestScoped -> SessionScoped no tiene ningún sentido y es un error.
- @Singleton se trata especialmente porque no puede tener ninguna dependencia de ámbito. Todos esperan que Singleton sea la "raíz".
El objetivo de esta combinación de reglas es hacer cumplir que cuando se aplica el alcance, los componentes se componen con la misma estructura que solíamos tener con Dagger 1.0 plus () ''d ObjectGraphs, pero con la capacidad de tener un conocimiento estático de todos los Encuadernaciones y sus alcances. Para decirlo de otra manera, cuando se aplican los ámbitos, esto limita los gráficos que solo se pueden construir a los que se pueden construir correctamente.
De mi propia práctica, es más claro no usar @Singleton
en absoluto. En lugar de eso, uso @ApplicationScope
. Sirve para definir singletons en toda la aplicación y no tiene restricciones adicionales como @Singleton
tiene.
Espero que te ayude :). Es bastante difícil de entender rápidamente, lleva tiempo, para mí al menos lo fue.