Can I use some kind of assisted Inject with Dagger?
Asked Answered
O

3

2

With Google Guice or Gin I can specify parameter with are not controlled by the dependency injection framework:

class SomeEditor {


  @Inject
  public SomeEditor(SomeClassA a, @Assisted("stage") SomeClassB b) {
  }

}

The assisted parameter stage is specified at the time an instance of SomeEditor is created.

The instance of SomeClassA is taken from the object graph and the instance of SomeClassB is taken from the caller at runtime.

Is there a similar way of doing this in Dagger?

Overtrump answered 20/3, 2015 at 20:8 Comment(2)
Could you be more precise?Nipping
The instance of SomeClassA is taken from the object graph and the instance of SomeClassB is taken from the caller at runtime.Overtrump
R
5

UPDATE: As of Dagger 2.31 from January 2021, Dagger now natively supports assisted injection, which is recommended over the Square and Auto options. (Those other options still work, but may require extra setup compared to the native option.)

class SomeEditor {
  @AssistedInject public SomeEditor(
      SomeClassA a, @Assisted SomeClassB b) {
    // ...
  }
}

@AssistedFactory interface SomeEditorFactory {
  SomeEditor create(SomeClassB b);
}

(original answer)

Because factories are a separate type of boilerplate to optimize away (see mailing list discussion here), Dagger leaves it to a sister project, AutoFactory. This provides the "assisted injection" functionality Guice offers via FactoryModuleBuilder, but with some extra benefits:

  • You can keep using AutoFactory with Guice or Dagger or any other JSR-330 dependency injection framework, so you can keep using AutoFactory even if you switch between them.
  • Because AutoFactory generates code, you don't need to write an interface to represent the constructor: AutoFactory will write a brand new type for you to compile against. (You can also specify an interface to implement, if you'd prefer, or if you're migrating from Guice.)
  • Because all the type inspection happens at compile-time, it produces plain old Java, which doesn't have any slowness due to reflection and which works well with debuggers and optimizers. This makes the Auto library particularly useful for Android development.

Example, pulled from AutoFactory's README, which will produce a SomeClassFactory with providedDepA in an @Inject-annotated constructor and depB in a create method:

@AutoFactory
final class SomeClass {
  private final String providedDepA;
  private final String depB;

  SomeClass(@Provided @AQualifier String providedDepA, String depB) {
    this.providedDepA = providedDepA;
    this.depB = depB;
  }

  // …
}
Rhearheba answered 22/3, 2015 at 16:11 Comment(0)
M
1

Just like @xsveda, I also wrote an answer about this in this other question, which I'll also reproduce here.


Today, for assisted injection with Dagger you probably want to use AssistedInject. I wrote about it in this blogpost, but I'll add a full example here to make things easier.

First thing you need are the dependencies:

compileOnly 'com.squareup.inject:assisted-inject-annotations-dagger2:0.4.0'
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0'

Then here's how it can look like:

class ImageDownloader @AssistedInject constructor(
  private val httpClient: HttpClient,
  private val executorService: ExecutorService,
  @Assisted private val imageUrl: URL,
  @Assisted private val callback: ImageCallback
) {

  @AssistedInject.Factory
  interface Factory {
    fun create(imageUrl: URL, callback: ImageCallback): ImageDownloader
  }
}

First thing is that instead of annotating the constructor with @Inject, we annotate it with @AssistedInject. Then we annotate the parameters that will have to go through the factory, which is the opposite of what AutoFactory expects. Finally, we need an inner factory interface annotated with @AssistedInject.Factory that has a single method that receives the assisted parameters and returns the instance we're interested in.

Unfortunately, we still have an extra step here:

@AssistedModule
@Module(includes = [AssistedInject_AssistedInjectModule::class])
interface AssistedInjectModule

We don't necessarily need a dedicated module for it, even though that's a valid option. But we can also have those annotations in another module that is already installed in the component. The nice thing here is that we only need to do it once, and after that any factory will automatically become part of the graph.

With that, you can basically inject the factory and ask for your object as you'd normally do.

Metzler answered 9/4, 2019 at 16:38 Comment(0)
G
0

Yes, please check this Square project: square/AssistedInject

Currently it is not in 1.0 yet for purpose. They wait until Dagger will introduce a public API for registering those generated Module classes automatically - see this issue. With that you won't have to reference them in your Dagger code as in this example from README:

@AssistedModule
@Module(includes = AssistedInject_PresenterModule.class)
abstract class PresenterModule {}
Galton answered 11/12, 2018 at 7:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.