Dagger unable to inject generated classes
Hello all!
I'm trying to get Dagger to inject some classes from the Android Annotations project. Normally, I wouldn't think this would be an issue, as theoretically Dagger can just inject the classes generated by Android Annotations. However, as detailed in this SO question, that's not the case.
As a quick recap of the question, when trying to inject the generated class (using an @Provides in the module), Dagger fails at run-time with a message like the following:
05-10 18:35:48.658 8366-8366/com.bspeice.daggeraaexample.daggeraaexample E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.bspeice.daggeraaexample.daggeraaexample, PID: 8366
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.bspeice.daggeraaexample.daggeraaexample/com.bspeice.daggeraaexample.daggeraaexample.MainActivity}: java.lang.IllegalStateException: Errors creating object graph:
com.bspeice.daggeraaexample.daggeraaexample.AAPrefs_ has no injectable members. Do you want to add an injectable constructor? required by class com.bspeice.daggeraaexample.daggeraaexample.MainActivity
However, if I simply copy the class that would be injected into the main project, rename it (to avoid a name conflict), and point the module and Activity to the new class, then everything runs just fine.
Because of the second condition, I suspect this is an error with Dagger, and not with Android Annotations. That being said, I have no idea what would trigger this.
I have created a sample project to prove the issue is reproducible. You can find the project over here. Currently, the project will compile and run fine. But if you change the @Injected class to AAPrefs_ instead of AAPrefsBuild, and change the MainActivity as well, you will receive the above error.
Thank you, and let me know if there is anything else I can do to help.
The problem is that Dagger requires that classes that has @Provided annotation must have some kind of Injection on it's own. If class doesn't require any additional injection you need to add @Inject to default constructor. Source: https://github.com/square/dagger/blob/master/core/src/main/java/dagger/internal/Linker.java#L267
Since you are using AndroidAnnotations it looks that you cannot inject generated classes directly, you might create some proxy class that could be injected and responsible for creating AndroidAnnotations objects.
BTW why Dagger even requires those empty annotations for constructor?
The short answer to why Dagger requires @Inject constructors is seen in simple errors like this: String.
class Foo {
@Inject String string;
}
If permitted non-@Inject-annotated constructors, then we then use Foo,
and someone fails to bind @Provides String aString() {...} then you
will have String's default constructor invoked and you get an empty
string.
We saw a lot of those sorts of errors in Guice, where unintended injection of types happened with insane default conditions, so Guice added a "requireAtInjectConstructors()" configuration, and Dagger just made that the universal requirement.
I'll answer the SO question on SO.
I totally understand why Dagger needs the @Inject annotation. The thing is, I do have @Provides bound, and included in the module. The following code is from the project I referenced earlier.
Module: DaggerAAExampleModules.java
@Module (
injects = {
MainActivity.class,
DaggerAAExample.class
}
)
public class DaggerAAExampleModules {
@Provides
AAPrefs_ providePrefs() {
return new AAPrefs_(DaggerAAExample.getApplication());
}
}
MainActivity.java
public class MainActivity extends ActionBarActivity {
@Inject
AAPrefs_ prefs;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerAAExample.getApplication().inject(this);
setContentView(R.layout.activity_main);
}
// ... more code to do stuff here ...
DaggerAAExample.java
public class DaggerAAExample extends Application {
private ObjectGraph graph;
private static DaggerAAExample instance;
public DaggerAAExample() {
instance = this;
}
public static DaggerAAExample getApplication() {
return instance;
}
@Override
public void onCreate() {
graph = ObjectGraph.create(new DaggerAAExampleModules());
graph.inject(this);
}
public void inject(Object o) {
graph.inject(o);
}
}
I've included the relevant code. Now, the trick is, Android Annotations at compile-time generates the AAPrefs_.java file. Dagger appears to find it, and the ObjectGraph appears to build successfully at compile. However, at runtime, I get the error as shown above. This is all doing a debug build, so I can't imagine that Proguard is messing anything up. I'm making sure to use an @Provides so that Dagger knows how to create the object (though as mentioned on SO, I'm not quite using the Context correctly), but at runtime, Dagger can't seem to find the constructor.
All I need to do for the example to work is just copy the file Android Annotations created to the src/ folder, rename it, rename the injections, and everything works. No code changes beyond the class name. That's why I'm confused.
I apologize if we seem to be talking past each other, please let me know if there's anything else I can do to clarify the issue.
Yeah - so that code seems like it should work. Why it would be trying to implicitly-bind something that has a provides method is very odd. I'll try to play with the example project.
We see this internally on scary classes that you'd never want Dagger to instantiate (e.g., android.app.Application). While we know it will never be called in practice, it's a bit unsettling to see an invocation of its constructor in the generated code. On May 12, 2014 5:49 PM, "Christian Edward Gruber" [email protected] wrote:
Yeah - so that code seems like it should work. Why it would be trying to implicitly-bind something that has a provides method is very odd. I'll try to play with the example project.
— Reply to this email directly or view it on GitHubhttps://github.com/square/dagger/issues/410#issuecomment-42906130 .
It's not though, @JakeWharton. It seems like he's calling the constructor explicitly in an @Provides method. No? Or am I missing something obvious?
To Dagger it's the same thing. In our impl we pass the Application instance to the constructor of the module, store it in an instance field, and return it in the provides. It's not bad because it's never invoked, it's just not something I'd like to see in the generated code. On May 12, 2014 5:57 PM, "Christian Edward Gruber" [email protected] wrote:
It's not though, @JakeWharton https://github.com/JakeWharton. It seems like he's calling the constructor explicitly in an @Provides method. No? Or am I missing something obvious?
— Reply to this email directly or view it on GitHubhttps://github.com/square/dagger/issues/410#issuecomment-42906564 .
Correct me if I'm wrong then, but this is a legitimate issue, I'm not just using Dagger the wrong way? I'm going to try and see if using a factory in addition to the '@Provides' does anything.
I'm having the same issue. Any news ?
Bumped into the same issues with a difference the Dagger-generated classes don't see AA generated classes despite those all are successfully generated. Not to make confs overcomplicated thinking about getting rid AA.
I'm sorry - I'm not following - what do you mean "AA" generated classes?
On 4 September 2014 13:39, Eugene Beletskiy [email protected] wrote:
Bumped into the same issues with a difference the Dagger-generated classes don't see AA generated classes despite those all are successfully generated. Not to make confs overcomplicated thinking about getting rid AA.
— Reply to this email directly or view it on GitHub https://github.com/square/dagger/issues/410#issuecomment-54541217.
Meaning the classes generated by Android Annotations https://github.com/excilys/androidannotations
Yeah - your issue is quite different than the issue described in this bug.
On 5 September 2014 01:49, Eugene Beletskiy [email protected] wrote:
Meaning the classes generated by Android Annotations https://github.com/excilys/androidannotations
— Reply to this email directly or view it on GitHub https://github.com/square/dagger/issues/410#issuecomment-54599884.
@JakeWharton @DjBushido I have a doubt about this code
@Module ( injects = { MainActivity.class, DaggerAAExample.class } )
why we need to add injects {} in module or when or where we should use this annotation. What i understood is the methods which this module provides can be injectable into only this two classes defined into injects {} ? correct me if i am wrong ....
It also seems to be the case that it gives this error when you provide the same object in multiple modules. This is obvious an error of mine, but maybe it helps someone.