72

Say I have a class Util that takes in a object - an instance of class Validator.

Since I want to avoid instantiating the Validator class within Util, I pass it in via a constructor:

public class Util {

   @Inject
   public Util(Validator validator) {

   }


}

I have a module that provides the Validator instance:

@Provides
@Singleton
Validator provideValidator() {
    return Validator.getInstance();
}

and an instance of the Util class:

@Provides
Util provideUtil(Validator validator) {
    return new Util(validator);
}

I have a component wired up that would give me an instance of Util:

Util getUtil()

so within my activity, I could call it like:

Util myUtil = getComponent.getUtil();

All of that works fine - myUtil has a proper instance of Validator class when instantiated.

Now I want to pass in a String variable named address (which is user input via a UI). I want to change the constructor so I pass in both an instance of Validator and the user inputted String:

@Inject
public Util(Validator validator, String address) {

}

I just can't get my head around how to pass that 2nd parameter. Can someone tell me how?

Ideally, I want to instantiate Util like:

Util myUtil = getComponent.getUtil(txtAddress.getText());
Community
  • 1
  • 1
Jay Sidri
  • 6,271
  • 3
  • 43
  • 62

5 Answers5

124

I had the same question as you when I started looking into Dagger 2 a couple of weeks ago. I found information on this (and most other Dagger 2-related issues) difficult to come by, so I hope this helps!

The most basic answer is that you cannot. What you are looking for is something that is called assisted injection, and it is not part of Dagger 2. Some other dependency injection (DI) frameworks, such as Guice, do offer this feature, so you might look into those. Of course, there are still ways to do what you want to do using Dagger 2.

Factories factories factories

The standard way to do what you want to do in combination with DI is by using the Factory pattern. Basically, you create an injectable factory class that takes runtime parameters such as address as arguments to the object creation methods that it provides.

In your case, you would need a UtilFactory into which Dagger 2 injects a Validator upon instantation and which offers a method create(String address) that creates instances of Util. UtilFactory should keep a reference to the injected instance of Validator so that it has everything it needs to create an instance of Util in the create method.

Wring code for many such factories can be cumbersome. You should definitely take a look at AutoFactory, which eases some of the burden. Guice's assisted injection seems to work quite similar to Dagger 2 + AutoFactory (albeit with even nicer syntactic sugar).

More modules / components

I doubt this is something that you would like to do in this case, but you could just create a module that provides the address (and instantiate a new component). You do not have to create a new @Module class for every possible address. Instead, you can just pass the address as an argument to the constructor of the module. You could use the @BindsInstance-annotation as suggested by teano to achieve a similar result.

I am not sure if this is an anti-pattern or not. To me, this seems like an acceptable route in some cases, but only when you are actually using the same e.g. address for the initialisation of "many" objects. You definitely do not want to instantiate a new component and a new model for each object that requires injection. It is not efficient, and if you are not careful you will end up with more boilerplate code than without Dagger.

Do not (always) use DI: Injectables versus newables

Something that was immensely useful to me when learning about DI frameworks was the realisation that using a DI framework does not mean that you have to DI to initialise all of your objects. As a rule of thumb: inject objects that you know of at compile time and that have static relations to other objects; do not inject runtime information.

I think this is a good post on the subject. It introduces the concept of 'newables' and 'injectables'.

  • Injectables are the classes near the root of your DI graph. Instances of these classes are the kind of objects that you expect your DI framework to provide and inject. Manager- or service-type objects are typical examples of injectables.
  • Newables are objects at the fringes of your DI graph, or that are not even really part of your DI graph at all. Integer, Address etc. are examples of newables.

Broadly speaking, newables are passive objects, and there is no point in injecting or mocking them. They typically contain the "data" that is in your application and that is only available at runtime (e.g. your address). Newables should not keep references to injectables or vice versa (something the author of the post refers to as "injectable/newable-separation").

In reality, I have found that it is not always easy or possible to make a clear distinction between injectables and newables. Still, I think that they are nice concepts to use as part of your thinking process. Definitely think twice before adding yet another factory to your project!

In your case, I think it would make sense to treat Util as an injectable and the address as a newable. This means that the address should not be part of the Util class. If you want to use instance of Util for e.g. validating/... addresses, just pass the address that you want to validate as an argument to the validation/... method.

Update from 2021

Since the 2.31 version of Dagger 2, there is also a mechanism for assisted injection using @AssistedInject. You can see more in the documentation here. (Edited at the suggestion of Jay Sidri.)

Semafoor
  • 1,942
  • 1
  • 15
  • 13
  • Good to learn the differentation: Injectables and Newables. Wonderful answer which gave me clarity about my dependencies. – rpattabi Dec 02 '16 at 10:26
  • 4
    That last point needs to be said more, and I really like the term "newables". I'll have to borrow that. – Jeff Bowman Mar 15 '17 at 17:02
  • Regarding the first solution: How do you get the Validator dependency into the UtilFactory? Would UtilFactory become an "Injectable"/dependency of wherever an Util-instance is required and be "wired together" (in this case get the Validator dependency) at program start/initialization through the DI framework? Isn't that strange when Factories are scattered all around the program? – cobby Sep 19 '18 at 16:03
  • Or let me phrase it differently: Is there any way to not add the factory as a dependency to the class where it is used? – cobby Sep 20 '18 at 18:29
  • 1
    Without knowing the specifics of your application I'd say yes, you need a dependency to the factory object. The factory can be injected though. By creating a factory class you are separating everything that can / should be injected from what you want to add at runtime: dependencies to 'injectables' go into the factory class, dependencies to 'newables' are passed as parameters to the factory methods. The fact that the creation of an `Util` has certain dependencies does not change. If you think your classes have too many dependencies, that might mean that they are too tightly coupled. – Semafoor Sep 21 '18 at 13:58
  • @Semafoor Thanks for the clarification! It seems that using factories in Dagger like that is the solution to runtime dependencies like Spring's BeanFactory#getBean(String, Object...)? – cobby Sep 24 '18 at 07:28
  • I'm not familiar with Spring so I can't really help you there. If you'd, say, inject an instance of Object and pass a string as a runtime argument, that could work! Remember that it really is possible to overuse factories, even if they are automatically generated. Try and see what works for you! – Semafoor Sep 24 '18 at 14:37
  • @Semafoor I think I see the main problem with factories. Imagine that I'd need `Util` all around the program. Am I right to assume that the **only** way is to also provide a `UtilsFactory#get` method? Basically `UtilsFactory#create` method does not only return a `Util`, but also stores it in the factory-object, so that it can get returned at other places via `UtilsFactory#get`? Or how would I reuse that same Utils object I created once when it's not inside the dependency container? – cobby Sep 25 '18 at 16:51
  • Or is there a way to add a dependency (in this case `Util`) to the container at runtime and use somthing like Lazy as an injectable dependency in other classes? – cobby Sep 25 '18 at 16:59
  • If you need the same instance of Util all over your program, then what kind of information does it contain that you cannot inject? Usually, you can pass this information into the module when you create it (e.g. using @BindsInstance), allowing you to inject it into Util and to inject Util elsewhere. Your solution to implement Util as a singleton is definitely smelly. – Semafoor Sep 25 '18 at 20:27
  • At the time I create the component, it simply doesn't contain the runtime information yet. That's the problem. So I basically want to "set up" the **factory** at one place with a runtime information (beside the injectables through Dagger) with `UtilsFactory#create(Arg someArg)` and then use it all around the program (e.g. ÙtilsFactory#get()`). The "set up" of the factory (which can only be completed at runtime) and usage are two different things. – cobby Sep 26 '18 at 00:09
  • I disagree with "do not inject runtime information.". One of the key benefits of dagger is that you can inject runtime information quite easily. This is immensely helpful in Android, where the application context (a runtime object) is needed by a lot of dependencies. Same could be true of any other framework that creates objects at runtime. There's even a section in dagger dedicated to binding specific instances: https://dagger.dev/users-guide "Binding instances" See https://dagger.dev/api/latest/dagger/BindsInstance.html. That said, option 1 is probably what most people want to do :) – JavierIEH Mar 25 '20 at 21:51
20

You can change the component builder to inject instances. See: https://google.github.io/dagger/users-guide#binding-instances

In your case, you can call:

Util myUtil = DaggerMyComponent.builder().withAddress(txtAddress.getText()).build().getComponent().getUtil();

if MyComponent is defined as:

@Component(modules = UtilModule.class)
interface MyComponent{

    MyComponent getComponent();

    @Component.Builder
    interface Builder {

        @BindsInstance Builder withAddress(@Address String address); //bind address instance

        MyComponent build();

    }
}

And UtilModule:

@Module
class UtilModule{

    @Provides
    Util getUtil(Validator validator, @Address String address){ //inject address instance
        return new Util(validator, address);
    }

}

Validator must be provided with an @Inject annotated constructor or a @Provides annotated method in a module class passed to MyComponent's modules in the @Component annotation.

Update:

@Address is a Qualifier that can be defined as:

@java.lang.annotation.Documented
@java.lang.annotation.Retention(RUNTIME)
@javax.inject.Qualifier
public @interface Address {}

and can also be replaced with the @Named qualifier, e.g. @Named("Address").

See Dagger Guide for more information about qualifiers.

teano
  • 505
  • 5
  • 12
  • what is `@Address` ? – Anup Ammanavar Jul 04 '19 at 10:17
  • Answered in the answer above. – arungiri_10 Jul 08 '19 at 15:15
  • What does "answered in above answer mean". Answers move around. Please explain the originals of @Address. Does one need to create it first? Is it just a declared annotation somewhere or is there something unique about it? – Matt Wolfe Oct 14 '20 at 22:41
  • Yes, it's a declared annotation as in section Qualifiers on https://dagger.dev/dev-guide – teano Oct 15 '20 at 04:18
  • should change "MyComponent getComponent();" to "Util util();" in the interface MyComponent and the call logic is: "Util myUtil = DaggerMyComponent.builder().withAddress(...).build().util(); " – cn123h Mar 07 '21 at 16:31
3

when initiate Module, you can pass some parameters like this:

public NetServiceModule(String baseUrl, boolean isLogEnabled, CookieJar cookieJar) {
    this.mBaseUrl = baseUrl;
    this.mIsLogEnabled = isLogEnabled;
    this.mCookieJar = cookieJar;
}

And then get the component in "Container Class":

NetServiceComponent component = DaggerNetServiceComponent.builder()
            .netServiceModule(new NetServiceModule(baseUrl, mIsLogEnabled, cookieJar))
            .build();
    component.inject(this);

With Provides method to provide Injection which generate by some parameters if it need:

@Provides
Retrofit provideRetrofit(OkHttpClient httpClient, GsonConverterFactory gsonConverterFactory, NetCallAdapterFactory netCallAdapterFactory) {

    return new Retrofit.Builder()
            .client(httpClient)
            .baseUrl(mBaseUrl)
            .addConverterFactory(gsonConverterFactory)
            .addCallAdapterFactory(netCallAdapterFactory)
            .build();
}
Francis Shi
  • 395
  • 4
  • 8
2

Since the 2.31 version of Dagger 2, there is a mechanism for assisted injection. We can use @AssistedInject for that. You can see more in the documentation here.

Example:

We have a class that needs assisted inject:

class MyDataService {
  @AssistedInject
  MyDataService(DataFetcher dataFetcher, @Assisted Config config) {}
}

We use @AssistedInject to indicate that our constructor will need assisted inject and @Assisted to mark which arguments will be assisted injected.

Then we need to define a factory for assisted injection

@AssistedFactory
public interface MyDataServiceFactory {
  MyDataService create(Config config);
}

And finally Dagger will generate this factory for us and we will able to inject it in place we need to use our dependency.

class MyApp {
  @Inject MyDataServiceFactory serviceFactory;

  MyDataService setupService(Config config) {
    MyDataService service = serviceFactory.create(config);
    // ...
    return service;
  }
}
Sergey
  • 21
  • 3
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/29809810) – east1000 Sep 13 '21 at 11:18
0
@Inject
Usermodel uuser;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    userComponent dc = DaggeruserComponent.create();
    dc.injectMain(this);

    historymodel hm =  uuser.getHistorymodel();// get the models to pass user inputs 

    videoModel vm = uuser.getVideoModel();//  get the models to pass user inputs

    hm.setUid("userid ");


}

}