Use Dagger modules without the "injects" directive

spCoder picture spCoder · Dec 9, 2013 · Viewed 8.6k times · Source

I am trying to make Dagger work without the "injects" directive inside the @Module annotation. I am basing my test project on the Android Simple Dagger example

This is the part that is giving me problems:

@Module(
    injects = HomeActivity.class,
    complete = false
)
public class DemoModule {
  // TODO put your application-specific providers here!
}

(Edit): Which in my code is CTXModules.java

The part that I'd like to remove is the "injects = HomeActivity.class". I know I can mark my own modules with the @Inject annotation in the constructor to remove that part there, but somehow it doesn't work with the module that is added to the graph. With that line there, everything works just fine.

The reason I need this is because dagger will be implemented in a base-library project that will be the foundation for some projects that share a common code base and therefore at the moment or writing the this part of the code I don't know what classes will inject modules.

Is what I am trying to do even possible?

I assume it is possible because the Android Module class does not use that directive.

Hope it's clear enough. Thanks in advance!

(EDIT)

I should have mentioned it. In my module I remove "injects = HomeActivity.class" and add "library = true" like in the Android Module class. What happens then is that I get this error (my bad I not added it before):

12-10 09:21:16.807: E/AndroidRuntime(21783): FATAL EXCEPTION: main
12-10 09:21:16.807: E/AndroidRuntime(21783): Process: com.ef.daggertestproject, PID: 21783
12-10 09:21:16.807: E/AndroidRuntime(21783): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.ef.daggertestproject/com.ef.daggertestproject.MainActivity}: java.lang.IllegalArgumentException: No inject registered for members/com.ef.daggertestproject.MainActivity. You must explicitly add it to the 'injects' option in one of your modules.
12-10 09:21:16.807: E/AndroidRuntime(21783):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2176)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2226)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at android.app.ActivityThread.access$700(ActivityThread.java:135)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1397)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at android.os.Handler.dispatchMessage(Handler.java:102)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at android.os.Looper.loop(Looper.java:137)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at android.app.ActivityThread.main(ActivityThread.java:4998)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at java.lang.reflect.Method.invokeNative(Native Method)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at java.lang.reflect.Method.invoke(Method.java:515)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at dalvik.system.NativeStart.main(Native Method)
12-10 09:21:16.807: E/AndroidRuntime(21783): Caused by: java.lang.IllegalArgumentException: No inject registered for members/com.ef.daggertestproject.MainActivity. You must explicitly add it to the 'injects' option in one of your modules.
12-10 09:21:16.807: E/AndroidRuntime(21783):    at dagger.ObjectGraph$DaggerObjectGraph.getInjectableTypeBinding(ObjectGraph.java:281)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at dagger.ObjectGraph$DaggerObjectGraph.inject(ObjectGraph.java:258)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at com.ef.daggertestproject.MyApplication.inject(MyApplication.java:47)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at com.ef.daggertestproject.BaseActivity.onCreate(BaseActivity.java:27)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at com.ef.daggertestproject.MainActivity.onCreate(MainActivity.java:16)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at android.app.Activity.performCreate(Activity.java:5243)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
12-10 09:21:16.807: E/AndroidRuntime(21783):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2140)
12-10 09:21:16.807: E/AndroidRuntime(21783):    ... 11 more

Also, I've uploaded my test project to github

(Final Edit) according to Jake's answer: "Declaring a module as a library does not alleviate the needs of Dagger to know about injection points." And therefore the answer to my original question is that it's not possible.

Answer

Jake Wharton picture Jake Wharton · Dec 10, 2013

You want

@Module(library=true)

Here's what the docs say about library:

False if all the included bindings in this module are necessary to satisfy all of its injectable types. If a module is not a library module, it is eligible for additional static checking: tools can detect if included bindings are not necessary. If you provide bindings that are not used by this module's graph, then you must declare library = true.

(emphasis mine)


Declaring a module as a library does not alleviate the needs of Dagger to know about injection points. You still must declare a module in the object graph with the listed injects.

An extreme simplified version of your example would look like this:

repo/
 +- library/
 |   +- Foo.java
 |   `- FooModule.java
 |
 `- app/
     +- BarActivity.java
     `- BarModule.java

FooModule.java:

@Module(library = true)
public final class FooModule {
  @Provides @Singleton provideFoo() {
    return Foo();
  }
}

BarModule.java:

@Module(
  injects = BarActivity.class,
  includes = FooModule.class
)
public final class BarModule {
}

In BarActivity.java (or similar):

ObjectGraph og = ObjectGraph.create(new BarModule());
og.inject(this);