java - support - Buscando un ejemplo para la inyección asistida Dagger
inject class android (1)
De dagger-discuss @ :
Tengo una clase que obtiene algunas dependencias del gráfico de objetos y otras dependencias de un llamador en tiempo de ejecución.
public class ImageDownloader {
// Get these dependencies from the injector.
private final HttpClient httpClient;
private final ExecutorService executorService;
// Get these from the caller.
private final URL imageUrl;
private final ImageCallback callback;
...
}
Se me ocurrió una solución, donde defino una fábrica,
public class ImageDownloader {
...
public static class Factory {
private final HttpClient httpClient;
private final ExecutorService executorService;
@Inject
public Factory(HttpClient httpClient, ExecutorService executorService) {
this.httpclient = httpClient;
this.executorService = executorService;
}
public ImageDownloader create(URL imageUrl, ImageCallback callback) {
return new ImageDownloader(httpClient, executorService, iamgeUrl, callback);
}
}
...
}
Ahora, en lugar de inyectar ImageDownloader
en el constructor del cliente, simplemente ImageDownloader.Factory
y llamo a su método create()
.
Como pueden ver, es bastante detallado y largo. También tiene un montón de duplicaciones y repeticiones. Hay algunos obstáculos para anotar los campos con @Inject
, así que ignoremos esta posibilidad por el momento.
La gente de Square ha encontrado una solución interesante, usando proveedores. Definir una interfaz de Factory
,
public class ImageDownloader {
...
public interface Factory {
ImageDownloader create(URL imageUrl, ImageCallback callback);
}
}
y luego proporcionarlo en un módulo,
public class ImageModule {
...
@Provides
public ImageModule.Factory provideImageModuleFactory(
final Provider<HttpClient> httpClientProvider,
final Provider<ExecutorService> executorServiceProvider) {
return new ImageDownloader.Factory() {
public ImageDownloader create(URL imageUrl, ImageCallback callback) {
return new ImageDownloader(httpClientProvider.get(), executorServiceProvider.get(),
imageUrl, callback);
}
}
...
}
(de nuevo, desde dagger-discuss @).
Mi ImageDownloader
es una clase que es inyectada por una clase que es inyectada por otra clase que es inyectada por otra clase, ..., a la que se hace referencia en un @Module
. Todo esto de alguna manera * funciona, y todas las clases se encuentran en tiempo de compilación. Ahora, para agregar un módulo, debo dejar explícitamente que el gráfico objeto lo sepa.
Debo extrañar algo: es muy fácil inyectar una nueva clase, pero es muy tedioso agregar un nuevo módulo.
Mi pregunta es: ¿cómo se hace la inyección asistida en la práctica? alguien tiene un ejemplo? ¿cómo debo usar ImageModule
, si es que lo hago?
* - "de alguna manera" de hecho implica que es parcialmente mágico para mí.
Entonces, algunos de los amigos de Dagger / Guice en Google crearon una cosa llamada AutoFactory ( http://github.com/google/auto ) en un proyecto que incluye AutoFactory (inyección asistida generada por código), AutoValue (valor personalizado generado por código) tipos) y AutoService (autogeneración de archivos de metadatos de servicios Java).
AutoFactory prácticamente funciona como era de esperar: genera la fábrica que de otro modo habrías rodado a mano. Es una versión muy temprana, y tenemos mucha más flexibilidad planeada, pero generará una clase de fábrica que tomará un tipo que incluye algunas dependencias inyectables JSR-330 y algunos parámetros de pila de llamadas, y los fusionará para crear instancias de el tipo anotado
En esencia, generará la fábrica que escribió, automáticamente si anota adecuadamente su tipo creado en fábrica.
Por ejemplo, si creas tu clase:
@AutoFactory
public class ImageDownloader {
// Get these dependencies from the injector.
private final HttpClient httpClient;
private final ExecutorService executorService;
// Get these from the caller.
private final URL imageUrl;
private final ImageCallback callback;
ImageDownloader(
@Provided HttpClient httpClient,
@Provided ExecutorService executorService,
ImageCallback callback,
URL imageUrl) {
// assignments
}
}
AutoFactory generará:
@Generated("com.google.auto.factory.processor.AutoFactoryProcessor")
public final class ImageDownloaderFactory {
private final Provider<ExampleClasses.HttpClient> httpClientProvider;
private final Provider<java.util.concurrent.ExecutorService> executorServiceProvider;
@Inject
public ImageDownloaderFactory(
Provider<ExampleClasses.HttpClient> httpClientProvider,
Provider<java.util.concurrent.ExecutorService> executorServiceProvider) {
this.httpClientProvider = httpClientProvider;
this.executorServiceProvider = executorServiceProvider;
}
public ImageDownloader create(ImageCallback callback, URL imageUrl) {
return new ImageDownloader(
httpClientProvider.get(),
executorServiceProvider.get(),
callback,
imageUrl);
}
}
(Tenga en cuenta que tenemos un montón de tareas de limpieza en la fuente de salida, pero lo anterior es básicamente lo que se genera, aunque no tan bien formateado).
La clase resultante es entonces, correctamente una clase inyectable compatible con JSR-330, que puede inyectar en su gráfico de dependencia (en Dagger o Guice) y creará estos objetos para usted, combinando el estado de la pila de llamadas con las dependencias proporcionadas. adecuadamente.
Puede inyectar el Just-In-Time anterior, o puede proporcionarlo a través del método @Provides
en su tiempo libre.
Incluso puede hacer que la fábrica implemente una interfaz de fábrica, y luego simplemente unir los dos en un módulo de daga de la siguiente manera:
@AutoFactory(implementing = MyFactoryInterface.class)
public class ImageDownloader {
// ... otherwise as above...
}
@Module(...)
class MyModule {
@Provides MyFactoryInterface factoryImpl(ImageDownloaderFactory impl) {
return impl;
}
}