Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Looking for an example for Dagger assisted injection

Tags:

From dagger-discuss@:

I have a class that gets some dependencies from the object graph, and other dependencies from a caller at runtime.

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;    ... } 

I came up with a solution, where I define a Factory,

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);     }   }   ... } 

Now, instead of injecting ImageDownloader in the client's constructor, I simply inject ImageDownloader.Factory and call its create() method.

As you can see, that's quite verbose and long. It also has a bunch of duplication and boilerplate. There're some obstacles to annotating the fields themselves with @Inject, so let's ignore this possibility for now.

The Square people have come up with an interesting solution, using providers. Define a Factory interface,

public class ImageDownloader {   ...   public interface Factory {     ImageDownloader create(URL imageUrl, ImageCallback callback);   } } 

and then provide it in a module,

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);       }   }   ... } 

(again, from dagger-discuss@).

My ImageDownloader is a class that's injected by a class which is injected by another class which is injected by yet another class, ..., which is referenced in a @Module. This all somehow* works, and all classes are found in build time. Now, to add a module, I have to explicitly let the object graph know about it.

I must be missing something - it's very easy to inject a new class, but very tedious to add a new module.

My question is: how is assisted injection done in practice? anyone has an example? how should I use ImageModule, if at all?

* - "somehow" does indeed imply it's partly magic to me.

like image 978
user1071136 Avatar asked Apr 02 '14 00:04

user1071136


People also ask

What is assisted injection dagger?

Dagger. Versions. Assisted injection is a dependency injection (DI) pattern that is used to construct an object where some parameters may be provided by the DI framework and others must be passed in at creation time (a.k.a “assisted”) by the user.

When would you use a field injection in a dagger?

One of the considerations with Dagger is that injected fields cannot be private. They need to have at least package-private visibility like in the preceding code. Note: Field injection should only be used in Android framework classes where constructor injection cannot be used.

When would you use assisted inject?

AssistedInject allows you to dynamically configure Factory for class instead of coding it by yourself. This is often useful when you have an object that has a dependencies that should be injected and some parameters that must be specified during creation of object.

What is dagger and its uses?

Dagger automatically generates code that mimics the code you would otherwise have hand-written. Because the code is generated at compile time, it's traceable and more performant than other reflection-based solutions such as Guice. Note: Use Hilt for dependency injection on Android.


1 Answers

So, some of the Dagger/Guice folks at Google created a thing called AutoFactory (http://github.com/google/auto) in a project that includes AutoFactory (code-generated assisted injection), AutoValue (code-generated custom value types) and AutoService (auto-generation of java services metadata files).

AutoFactory pretty much operates like you would expect - it generates the factory you would otherwise have hand-rolled. It's a very early version, and we have a lot more flexibility planned, but it will generate a factory class that will take a type that includes some JSR-330 injectable dependencies and some call-stack parameters, and merge them together in creating instances of the annotated type.

In essence it will generate the factory you wrote, automatically if you properly annotate your factory-created type.

For instance, if you create your class:

@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 will generate:

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

(Note, we have a bunch of clean-up to do on the output source, but the above is basically what is generated, though not quite as nicely formatted.)

The resulting class is then, properly a JSR-330 compliant injectable class, which you can inject in your dependency graph (in Dagger or Guice) and it will create these objects for you, co-mingling the call-stack state with the provided dependencies appropriately.

You can inject the above Just-In-Time, or you can provide it via an @Provides method at your leisure.

You can even have the factory implement a factory interface, and then simply bind the two together in a dagger module like so:

@AutoFactory(implementing = MyFactoryInterface.class) public class ImageDownloader {   // ... otherwise as above... }  @Module(...) class MyModule {   @Provides MyFactoryInterface factoryImpl(ImageDownloaderFactory impl) {     return impl;   } } 
like image 144
Christian Gruber Avatar answered Oct 21 '22 02:10

Christian Gruber