12

Is it possible to inject something into a module?

I have 2 base modules / components:

@Component(modules = {OkHttpModule.class})
public interface OkHttpComponent extends AppComponent {

    OkHttpClient provideOkHttpClient();
}

@Module
public class OkHttpModule {

    @Provides
    OkHttpClient provideOkHttpClient() {

        return mHttpClient;
    }
}
@Component(modules = {GsonModule.class})
public interface GsonComponent extends AppComponent {

    Gson provideGson();
}

@Module
public class GsonModule {

    @Provides
    Gson provideGson() {

        return mGson;
    }
}

With this modules / components I want to create a 3rd module:

@Component(dependencies = {OkHttpComponent.class, GsonComponent.class}, 
           modules = {RetrofitModule.class})
public interface RetrofitComponent extends AppComponent {

    API provideAPI();
}

@Module
public class RetrofitModule {

    @Inject OkHttpClient mHttpClient;
    @Inject Gson         mGson;

    @Provides
    VeentsMeAPI provideVeentsMeAPI() {

        return mVeentsMeAPI;
    }
}

But mHttpClient and mGson are not injected. Is it possible to inject something into a module? And if yes how?

I create the components like this:

okHttpComponent = DaggerOkHttpComponent.builder()
        .okHttpModule(new OkHttpModule(this))
        .build();

gsonComponent = DaggerGsonComponent.builder()
        .gsonModule(new GsonModule())
        .build();

retrofitComponent = DaggerRetrofitComponent.builder()
        .retrofitModule(new RetrofitModule())
        .okHttpComponent(okHttpComponent)
        .gsonComponent(gsonComponent)
        .build();
Ralph Bergmann
  • 3,015
  • 4
  • 30
  • 61
  • What are you using `mHttpClient` and `mGson` used for in your module? If they're needed to provide a dependency, can you add them as `@Provides` method parameters instead? If you're keeping them to provide singleton behavior, can they just be annotated with `@Singleton`? – Jeff Bowman May 26 '15 at 18:11
  • Hi @JeffBowman I'm new to Dagger2 maybe I'm a little bit wrong. At the end I want to inject the Retrofit RestAdapter. – Ralph Bergmann May 26 '15 at 19:45

1 Answers1

23

In my opinion, there isn't really a point to injecting into a module. All modules are declared as complete=false, library=true as per the Dagger1 definition of modules, meaning you don't need to specify any real magic as long as you include the modules that are not contained within the component you are using, or make your component dependencies link to each other in such a way that each dependency you can inject is specified only in one component.

You will get the dependencies from other modules in constructor parameters. You need to include the module on the includes list, or you need to have the module specified in your component within the same scope.

The right way to do it is this:

@Module
public class OkHttpModule {
    @Provides
    @ApplicationScope
    public OkHttpClient okHttpClient() {
        return new OkHttpClient();
    }
}

@Module
public class GsonModule {
    @Provides
    @ApplicationScope 
    public Gson gson() {
        return new Gson();
    }
}

@Module(includes={GsonModule.class, OkHttpModule.class}) //look closely, this is the magic
public class RetrofitModule {
    @Provides
    @ApplicationScope
    public VeentsMeAPI veentsMeAPI(Gson gson, OkHttpClient okHttpClient) { //dependencies!
         return RestAdapter.Builder()
            .setClient(new Client(okHttpClient))
            .setConverter(new GsonConverter(gson))
            .create(VeentsMeAPI.class);
    }
}

And then it's

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {}

And maybe even

@ApplicationScope
public class Something {
    private final VeentsMeAPI veentsMeApi;

    @Inject
    public Something(VeentsMeAAPI veentsMeApi) {
        this.veentsMeApi = veentsMeApi;
    }
}

Then

@ApplicationScope
@Component(modules={RetrofitModule.class}) //contains all other modules/dependencies
public interface AppComponent {
    OkHttpClient okHttpClient();
    Gson gson();
    VeentsMeAPI veentsMeAPI();
    Something something();

    void inject(SomeActivity someActivity);
}

and

Then you'd get the generated stuff and you'd have to build it together like so

AppComponent appComponent = DaggerAppComponent.create();

//use appComponent.inject(someActivity); with `void inject(Stuff someActivity);` methods defined in AppComponent

Component dependencies are the same as subcomponents, so those are for subscoping, and not for inheriting dependencies from the same scope. Dependencies of the same scope should be in the same component, just different modules.

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • 2
    The `AppComponent` is only a empty interface for the right scope, see http://stackoverflow.com/a/30425419/1016472 This `RetrofitComponent retrofitComponent = DaggerRetrofitModule.builder() .okHttpComponent(okHttpComponent) .gsonComponent(gsonComponent) .retrofitModule(retrofitModule) .build();` was what I did but I hoped that there is a better way. – Ralph Bergmann May 26 '15 at 21:35
  • 1
    I didn't know that the @Provide methods could have dependancies of their own, +1 for that. – Itai Hanski Nov 23 '15 at 08:29
  • @Ewoks and if you read the answer, it has `@Component` in it, so it can't be dagger1 – EpicPandaForce Oct 16 '16 at 13:18
  • @Ewoks it's fine :) I did compare it to Dagger1 at the start, but that's because in May 2015, that was still relevant for comparison. – EpicPandaForce Oct 16 '16 at 14:06
  • @EpicPandaForce Sorry, my bad. I read and saw Component, but thought that I saw something else not existing in Dagger2 but just Dagger1.. Probably I mixed it up with other post. Will delete comment. – Ewoks Oct 16 '16 at 14:07