Dagger 2: injecting a user-entered parameter into an object

Let's say I have a Util class that takes an object - an instance of the Validator class .

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

public class Util {

   @Inject
   public Util(Validator validator) {

   }


}

      

I have a module that provides a 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 connected that will give me a Util instance:

Util getUtil()

      

so in my activity I could call it like this:

Util myUtil = getComponent.getUtil();

      

This all works great - myUtil has a corresponding instance of the Validator class when instantiated.

Now I want to pass a string variable named address (which is entered by the user through the UI). I want to modify the constructor to pass both the Validator instance and the user-entered string:

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

}

      

I just can't figure out how to pass this second parameter. Can someone tell me how?

Ideally, I want to create a Util like:

Util myUtil = getComponent.getUtil(txtAddress.getText());

      

+39


source to share


4 answers


I had the same question as you when I started learning Dagger 2 a couple of weeks ago. I found information on this (and most other dagger 2 related questions) that are hard to find, so I hope this helps!

The simplest answer is you can't. What you are looking for is what is called helper injection and is not part of dagger 2. Some other Dependency Injection (DI) frameworks like Guice suggest this feature for you to learn. 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 conjunction with DI is to use the Factory pattern. Basically, you create an injectable Factory class that takes runtime parameters such as address

, as arguments to the object creation methods it provides.

In your case, you'll need UtilFactory

one that Dagger 2 injects into Validator

on creation and offers a method create(String address)

that instantiates Util

. UtilFactory

must contain a reference to the injected instance Validator

so that it has everything it needs to instantiate Util

in the method create

.

The code for many of these plants can be cumbersome. You should definitely take a look at AutoFactory , which eases some of the burden. It looks like Guice injection works very similar to Dagger 2 + AutoFactory (albeit with nicer syntactic sugar).

Additional modules / components

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



I'm 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 actually use the same one, eg. address to initialize "many" objects. You definitely don't want to instantiate a new component and a new model for every object that needs injection. It's not efficient, and if you're not careful, you'll end up with more boilerplate code than without the dagger.

Don't (always) DI: Injection versus innovation

Something that was very helpful to me while learning about DI frameworks was that using a DI framework doesn't mean you have to initialize all of your DI objects. Typically: introduce objects that you know at compile time and that have a static relationship with other objects; do not enter runtime information.

I think this is a good article on this topic. It introduces the concept of "novelty" and "injection".

  • Injections are classes near the root of your DI plot. Instances of these classes are the objects that you expect your DI infrastructure to provide and inject. Objects of type "manager" or "service type" are typical examples of injection.
  • Newables are objects at the edges of your DI plot, or are not part of your DI plot at all. Integer

    , address

    etc. are examples of new features.

Broadly speaking, novelty is passive objects, and there is no point in introducing or mocking them. They usually contain "data" that is in your application and is only available at runtime (for example, your address). Newables should not contain references to injection or vice versa (what the author refers to as "injection / new split").

In fact, I have found that it is not always easy or possible to make a clear distinction between injectables and new ones. However, I think they are good concepts to use as part of your thinking process. Definitely think twice before adding another Factory to your project!

In your case, I think it would be wise to treat it Util

as injectable and the address as new. This means that the address does not have to be part of the class Util

. If you want to use an instance Util

eg. validating / ... address, just pass the address you want to validate as an argument to the validation / ... method.

+81


source


You can change the bean builder to embed 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);
    }

}

      

The validator must be provided with an @Inject annotated constructor or an @Provides annotated method in the module class passed to the MyComponent modules in the @Component annotation.

+6


source


when initializing a module, you can pass some parameters, for example:

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

      

Using Provides a method for providing Injections that generate on some parameters, if needed:

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

    return new Retrofit.Builder()
            .client(httpClient)
            .baseUrl(mBaseUrl)
            .addConverterFactory(gsonConverterFactory)
            .addCallAdapterFactory(netCallAdapterFactory)
            .build();
}

      

+2


source


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


}

      

}

0


source







All Articles