Dagger 2: Inyectar el parámetro introducido por el usuario en el objeto
Digamos que tengo una clase Util que tiene en un objeto – una instancia de la clase Validator .
Puesto que quiero evitar instanciar la clase Validator dentro de Util, lo paso a través de un constructor:
- enviar y recibir sms para verificar el número de móvil
- Enviar eficientemente int a través de sockets en Java
- ¿El editor de diseño de Android Studio muestra propiedades de vista personalizada?
- Rxjava2 + Retrofit2 + Android. La mejor manera de hacer cientos de llamadas de red
- Xamarin y APK Firma - Cambiar ruta a JarSigner
public class Utils { @Inject public Util(Validator validator) { } }
Tengo un módulo que proporciona la instancia Validator:
@Provides @Singleton PhoneNumberUtil provideValidator() { return Validator.getInstance(); }
Y una instancia de la clase Util:
@Provides Util provideUtil(Validator validator) { return new Utils(validator); }
Tengo un componente cableado que me daría una instancia de Util:
Util getUtil()
Así que dentro de mi actividad, podría llamarlo así:
Util myUtil = getComponent.getUtil();
Todo eso funciona bien – myUtil tiene una instancia adecuada de la clase Validator cuando se instancia.
Ahora quiero pasar en una variable String nombre dirección (que es la entrada del usuario a través de una interfaz de usuario). Quiero cambiar el constructor así que paso en una instancia de Validator y el usuario inputted Cadena:
@Inject public Util(Validator validator, String address) { }
Simplemente no puedo obtener mi cabeza alrededor de cómo pasar ese segundo parámetro. ¿Puede alguien decirme cómo?
Idealmente, quiero instanciar Util como:
Util myUtil = getComponent.getUtil(txtAddress.getText());
- Cuántas llamadas de función causarán desbordamiento de pila
- ¿Por qué no se prefieren los getters al acceder a variables entre actividades?
- Android: Solicitar al usuario que guarde los cambios cuando se pulsa el botón Atrás
- ¿Qué podría hacer la carga lenta en android 4.1.X y no en 4.2?
- Bluetooth no se conecta en 4.4.2
- Android obtiene PID de otras aplicaciones
- La Interfaz de JavaScript de WebView sólo parece funcionar a través de Instant-Run de Android Studio, pero no cuando se compila a través de gradle regular
- Java SIP Plugin para Cordova o Phonegap para streaming de audio y video
Tuve la misma pregunta que tú cuando empecé a buscar Dagger 2 hace un par de semanas. Encontré información sobre esto (y la mayoría de otros temas relacionados con Dagger 2) difícil de conseguir, así que espero que esto ayude!
La respuesta más básica es que no se puede. Lo que usted está buscando es algo que se llama inyección asistida , y no es parte de Daga 2. Algunos otros marcos de inyección de dependencia (DI), como Guice , ofrecen esta característica, por lo que podría mirar en ellos. Por supuesto, todavía hay maneras de hacer lo que quieres hacer usando Daga 2.
Fábricas fábricas fábricas
La forma estándar de hacer lo que se desea hacer en combinación con DI es utilizando el patrón de fábrica. Básicamente, se crea una clase de fábrica inyectable que toma parámetros de tiempo de ejecución como address
como argumentos a los métodos de creación de objetos que proporciona.
En su caso, necesitaría un UtilFactory
en el que Dagger 2 inyecta un Validator
al instante y que ofrece un método create(String address)
que crea instancias de Util
. UtilFactory
debe mantener una referencia a la instancia inyectada de Validator
para que tenga todo lo necesario para crear una instancia de Util
en el método create
.
El código de retorcimiento de muchas de estas fábricas puede ser engorroso. Usted debe definitivamente echar un vistazo a AutoFactory , que alivia parte de la carga. La inyección asistida de Guice parece funcionar bastante similar a Dagger 2 + AutoFactory (aunque con azúcar sintáctico aún más agradable).
Más módulos / componentes
Dudo que esto sea algo que le gustaría hacer en este caso, pero podría crear un módulo que proporcione la dirección (e instanciar un nuevo componente). No es necesario crear una nueva clase @Module para cada dirección posible. En su lugar, sólo puede pasar la dirección como un argumento al constructor del módulo.
No estoy seguro si esto es un anti-patrón o no. Para mí, esto parece ser una ruta aceptable en algunos casos, pero sólo cuando está utilizando la misma dirección por ejemplo para la inicialización de "muchos" objetos. Definitivamente no desea instanciar un nuevo componente y un nuevo modelo para cada objeto que requiere inyección. No es eficiente, y si usted no es cuidadoso usted terminará para arriba con un código más boilerplate que sin daga.
No (siempre) usar DI: Inyectables versus nuevos
Algo que me resultó inmensamente útil cuando aprendí acerca de los marcos DI fue la comprensión de que el uso de un marco DI no significa que usted tiene que DI para inicializar todos sus objetos. Como regla general: inyecte objetos que conozca en tiempo de compilación y que tengan relaciones estáticas con otros objetos; No inyecte información de tiempo de ejecución.
Creo que este es un buen post sobre el tema. Introduce el concepto de "nuevos" e "inyectables".
- Inyectables son las clases cerca de la raíz de su gráfico DI. Las instancias de estas clases son el tipo de objetos que usted espera que su marco DI proporcione e inyecte. Los objetos de tipo de servicio o de servicio son ejemplos típicos de inyectables.
- Los objetos nuevos son objetos en las franjas de su gráfico DI, o que no son realmente parte de su gráfico DI en absoluto.
Integer
,Address
etc. son ejemplos de elementos nuevos.
Hablando en términos generales, los objetos nuevos son objetos pasivos, y no tiene sentido inyectarse o burlarse de ellos. Por lo general contienen los "datos" que están en su aplicación y que sólo está disponible en tiempo de ejecución (por ejemplo, su dirección). Los objetos nuevos no deben guardar referencias a los inyectables o viceversa (algo que el autor del post denomina "inyectable / nueva-separación").
En realidad, he encontrado que no siempre es fácil o posible hacer una distinción clara entre inyectables y nuevos. Sin embargo, creo que son buenos conceptos para usar como parte de su proceso de pensamiento. ¡Definitivamente piensa dos veces antes de agregar otra fábrica a tu proyecto!
En su caso, creo que tendría sentido tratar Util
como un inyectable. Esto significa que la dirección no debe formar parte de la clase Util
. Si desea utilizar la instancia de Util
para, por ejemplo, validating / … addresses, simplemente pase la dirección que desea validar como argumento al método de validación / ….
Cuando inicie el módulo, puede pasar algunos parámetros como este:
public NetServiceModule(String baseUrl, boolean isLogEnabled, CookieJar cookieJar) { this.mBaseUrl = baseUrl; this.mIsLogEnabled = isLogEnabled; this.mCookieJar = cookieJar; }
Y luego obtenga el componente en "Clase de contenedor":
NetServiceComponent component = DaggerNetServiceComponent.builder() .netServiceModule(new NetServiceModule(baseUrl, mIsLogEnabled, cookieJar)) .build(); component.inject(this);
Con Proporciona el método para proporcionar Inyección que genera por algunos parámetros si es necesario:
@Provides Retrofit provideRetrofit(OkHttpClient httpClient, GsonConverterFactory gsonConverterFactory, NetCallAdapterFactory netCallAdapterFactory) { return new Retrofit.Builder() .client(httpClient) .baseUrl(mBaseUrl) .addConverterFactory(gsonConverterFactory) .addCallAdapterFactory(netCallAdapterFactory) .build(); }
Puede cambiar el constructor de componentes para inyectar instancias. Consulte: https://google.github.io/dagger/users-guide#binding-instances
En su caso, puede llamar a:
Util myUtil = DaggerMyComponent.builder().withAddress(txtAddress.getText()).build().getComponent().getUtil();
Si MyComponent se define como:
@Component(modules = UtilModule.class) interface MyComponent{ MyComponent getComponent(); @Component.Builder interface Builder { @BindsInstance Builder withAddress(@Address String address); MyComponent build(); } }
Y UtilModule:
@Module class UtilModule{ @Provides Util getModule(Validator validator, @Address String address){ return new Util(validator, address); } }
Validador, por supuesto, se debe proporcionar con @Inject anotado constructor o @Provides un método anotado en una clase de módulo pasó a los módulos de MyComponent en la anotación @Component.