Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Module depending on another module in Dagger

I'm trying to use Dagger to do Dependency Injection on an app that I'm building, and running into trouble constructing proper DAGs when I have one package's Module depending on values provided by the Injector (presumably provided by another Module).

If I have a simple module for some configurable variables (that I might want to swap out for testing environments, for example)

@Module(
    injects = DependentModule.class,
)
public class ConfigModule {

    @Provides @Named("ConfigOption") String provideConfigOption() {
        return "This Module's configurable option!";
    }
}

and another module depends on it, e.g.

@Module(
    injects = {
            TopLevelClass.class
    }
)
public class DependentModule {

    @Inject @Named("ConfigOption") String configOption;

    public DependentModule() {
        ObjectGraph.create(this).inject(this);
        doSomethingWithConfig(configOption);
    }

    @Provides @Singleton UsefulValue provideUsefulValue() {
        // Whatever this module needs to do...
    }
}

The line where I try to bootstrap the injection in the constructor fails, and it complains that I haven't specified an explicit injects line in a proper module.

Through trial-and-error I see this goes away if in @Module I add a line include = ConfigModule.class, but this strikes me as semantically wrong, since a) the DAG I'll be creating will now include the values of both modules, rather than just one, and b) it defeats the purpose/flexibility of DI in the first place to link a specific Module rather than simply let Dagger inject the appropriate value.

I'm presuming I shouldn't be creating an Object Graph with this only to inject into it? But then I run into the issue of not linking a specific Module...

Succinctly:

  • What is the 'proper' way to Inject values into one Modules that may be provided from other Modules? Here I'm using field injection, but my experiments with constructor injection have also resulted in a lot of failure.
  • Relatedly, when is it appropriate to use addsTo vs. includes?

Thanks :)

like image 394
pablo.meier Avatar asked Jul 10 '14 05:07

pablo.meier


People also ask

What is dependency injection Dagger?

The term dependency injection context is typically used to describe the set of objects which can be injected. In Dagger 2, classes annotated with @Module are responsible for providing objects which can be injected. Such classes can define methods annotated with @Provides .

Can Dagger have multiple components?

Reusability is one of the many attributes that are said to contribute to a high quality code base. Dagger is in many aspects a tool which helps a lot with writing reusable code.

What is subcomponent in Dagger?

In Dagger 2, component is the main container-like object that binds all the dependencies (or it's factory). Subcomponent are components that is like an extension to its parent component. It can be used for. Partition the dependencies into different compartments.

Is Dagger 2 deprecated?

It's officially deprecated and you can pretty much ignore it. Google's framework, which became dominant in Android ecosystem, was originally called Dagger 2. Sometimes we still refer to it as such, but, in most cases, we simply call it Dagger today.


1 Answers

You don't need to do any of injection (field or constructor) in one module from another explicitly. Just use addsTo and includes. includes allows to add modules to another and use everything they provide. Example:

@Module()
public class ModuleA {
    @Provides @Named("ValueA") String provideValueA() {
        return "This is ValueA";
    }
}

@Module(
    includes = ModuleA.class
)
public class ModuleB {
    // ValueA comes from ModuleA
    @Provides @Named("ValueB") String provideValueB(@Named("ValueA") String valueA) {
        return valueA + " and ValueB";
    }
}

addsTo is used with ObjectGraph.plus(Object... modules). When graph is already created and contains some modules (e.g. in Application class), you can create new graph (e.g. in Activity) using plus. Example:

@Module()
public class ApplicationModule {
    @Provides @Named("ValueA") String provideValueA() {
        return "This is ValueA";
    }
}

@Module(
    addsTo = ApplicationModule.class
)
public class ActivityModule {
    // ValueA comes from ApplicationModule
    @Provides @Named("ValueB") String provideValueB(@Named("ValueA") String valueA) {
        return valueA + " and ValueB";
    }
}

public class DemoApplication extends Application {
  private ObjectGraph graph;

  @Override public void onCreate() {
     super.onCreate();
     graph = ObjectGraph.create(getModules().toArray());
  }

  protected List<Object> getModules() {
      return Arrays.asList(
          new ApplicationModule()
      );
  }

  public void inject(Object object) {
      graph.inject(object);
  }

  public ObjectGraph getObjectGraph() {
      return graph;
  }
}

public class DemoActivity extends Activity {
    private ObjectGraph activityGraph;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Create the activity graph by .plus-ing our modules onto the application graph.
        DemoApplication application = (DemoApplication) getApplication();
        activityGraph = application.getApplicationGraph().plus(new ActivityModule());

        // Inject ourselves so subclasses will have dependencies fulfilled when this method returns.
        activityGraph.inject(this);
    }

    @Override protected void onDestroy() {
        // Eagerly clear the reference to the activity graph to allow it to be garbage collected as
        // soon as possible.
        activityGraph = null;
        super.onDestroy();
    }
}

Also you can check this example to create scopes of graphs.

like image 86
Kirill Boyarshinov Avatar answered Oct 07 '22 14:10

Kirill Boyarshinov