Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dagger Module Inclusion Cycle

Tags:

android

dagger

I'm still new to Dagger and trying to get a hang of things. I wanted to split my modules into logical groups which each provide their own functionality, yet basically would act the same as if it were in one Module.

For instance, let's say I have my main application module defined as follows:

//com.example.android.MyAppModule.java
@Module(
   includes = AnalyticsModule.class,
   injects = { <snip> }
)
public class MyAppModule {
   // various provides
}

And I have another module defined like this which sets up an ErrorReporter interface and provides the concrete implementation to it.

// com.example.android.analytics.AnalyticsModule.java
@Module(
   addsTo = MyAppModule.class,
   injects = { MyApp.class }
)
public class AnalyticsModule(){

    // ErrorReporter is a public interface and ErrorReporterImpl is a package-local final concrete class that implements it
    @Provides @Singleton
    ErrorReporter providesErrorReporter(ErrorReporterImpl reporter) { return reporter };
}

In my Application class I set up the object graph like this:

// com.example.android.MyApp.java
public class MyApp extends Application {
    @Inject ErrorReporter errorReporter;

    @Override
    public void onCreate() {
        super.onCreate();
        applicationGraph = ObjectGraph
            .create(new MyAppModule())
            .plus(new AnalyticsModule());
        applicationGraph.inject(this);
        errorReporter.initialize();
    }
}

When I run the dagger compiler I get something like this:

Graph validation failed: Module Inclusion Cycle:
0. com.example.android.analytics.AnalyticsModule included by com.example.android.MyAppModule
1. com.example.android.modules.MyAppModule included by com.example.android.analytics.AnalyticsModule
0. com.example.android.analytics.AnalyticsModule

What am I doing wrong here? I assume it has something to do with includes/addsTo, but when I remove those I get other errors.

If I remove includes = AnalyticsModule.class from MyAppModule I get something like this:

com.example.android.analytics.ErrorReporter could not be bound with key com.example.android.analytics.ErrorReporter required by com.example.android.MyApp for com.example.android.MyAppModule

Everything is fine if I completely forgo an AnalyticsModule and then hand off the providesErrorReporter to MyAppModule, but then I have to make my concrete impl class public so I can use it in the other module.

like image 384
Paul Avatar asked Feb 03 '14 18:02

Paul


2 Answers

@Module(includes = AnalyticsModule.class) is useful for composing multiple modules into one module. In this case, using .plus(new AnalyticsModule()) is wrong because AnalyticsModule has already been included by the includes line in the annotation. So you should remove the call to plus().

@Module(addsTo = MyAppModule.class) is for allowing a call to .plus(new AnalyticsModule()). Since we've removed that call, we should remove the addsTo as well.

@Module(complete = false) is necessary for AnalyticsModule because some of its dependencies can't be satisfied on its own. This is okay; as long as MyAppModule has complete = true (the default), Dagger will do the necessary error checking.

Admittedly, the "Module Inclusion Cycle" error was a little bit unclear. The cycle was caused by A including B which addsTo A.

like image 59
Tavian Barnes Avatar answered Nov 03 '22 06:11

Tavian Barnes


addsTo= is there to specify that this graph is an extension to the referenced module in a parent/child graph. .plus() is the run-time version of this. You only need .plus() if you have shorter-lived graph-managed instances. This corresponds roughly to the notion of "scope" in Guice and other DI containers.

In your example you are doing:

applicationGraph = ObjectGraph
    .create(new MyAppModule())
    .plus(new AnalyticsModule());

This ends up creating two graphs in a parent/child relationship, which isn't what you need here. IN an android app you want one graph for the Application.

You can simply remove addsTo and MyAppModule will automatically instantiate AnalyticsModule, or you can pass both in if you want to avoid the dynamic initialization like so:

applicationGraph = ObjectGraph.create(new MyAppModule(), new AnalyticsModule());

The module inclusion cycle is because you have a cyclical relationship between these two modules and modules must themselves form an acyclic graph of configuration. MyAppModule includes AnalyticsModule which in turn includes MyAppModule. (addsTo is a less rigorous includes= used to specify things obtained from parent graphs)

like image 39
Christian Gruber Avatar answered Nov 03 '22 07:11

Christian Gruber