Skip to content
This repository has been archived by the owner on Aug 26, 2021. It is now read-only.

Dagger unable to inject generated classes #410

Open
bspeice opened this issue May 10, 2014 · 15 comments
Open

Dagger unable to inject generated classes #410

bspeice opened this issue May 10, 2014 · 15 comments

Comments

@bspeice
Copy link

bspeice commented May 10, 2014

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.

bspeice pushed a commit to MinimalBible/MinimalBible-Legacy that referenced this issue May 10, 2014
@MikolajKakol
Copy link

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?

@cgruber
Copy link
Collaborator

cgruber commented May 12, 2014

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.

@bspeice
Copy link
Author

bspeice commented May 12, 2014

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.

@cgruber
Copy link
Collaborator

cgruber commented May 13, 2014

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.

@JakeWharton
Copy link
Member

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" notifications@github.com
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//issues/410#issuecomment-42906130
.

@cgruber
Copy link
Collaborator

cgruber commented May 13, 2014

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?

@JakeWharton
Copy link
Member

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" notifications@github.com
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//issues/410#issuecomment-42906564
.

@bspeice
Copy link
Author

bspeice commented May 14, 2014

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.

@christophesmet
Copy link

I'm having the same issue.
Any news ?

@Yougin
Copy link

Yougin commented Sep 4, 2014

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.

@cgruber
Copy link
Collaborator

cgruber commented Sep 5, 2014

I'm sorry - I'm not following - what do you mean "AA" generated classes?

On 4 September 2014 13:39, Eugene Beletskiy notifications@github.com
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
#410 (comment).

@Yougin
Copy link

Yougin commented Sep 5, 2014

Meaning the classes generated by Android Annotations https://github.com/excilys/androidannotations

@cgruber
Copy link
Collaborator

cgruber commented Sep 5, 2014

Yeah - your issue is quite different than the issue described in this bug.

On 5 September 2014 01:49, Eugene Beletskiy notifications@github.com
wrote:

Meaning the classes generated by Android Annotations
https://github.com/excilys/androidannotations


Reply to this email directly or view it on GitHub
#410 (comment).

@dgandhi17
Copy link

@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 ....

@christophecommon
Copy link

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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants