dagger icon indicating copy to clipboard operation
dagger copied to clipboard

Dagger unable to inject generated classes

Open bspeice opened this issue 11 years ago • 15 comments

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 avatar May 10 '14 22:05 bspeice

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?

MikolajKakol avatar May 12 '14 18:05 MikolajKakol

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.

cgruber avatar May 12 '14 20:05 cgruber

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.

bspeice avatar May 12 '14 23:05 bspeice

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.

cgruber avatar May 13 '14 00:05 cgruber

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 .

JakeWharton avatar May 13 '14 00:05 JakeWharton

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?

cgruber avatar May 13 '14 00:05 cgruber

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 .

JakeWharton avatar May 13 '14 01:05 JakeWharton

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.

bspeice avatar May 14 '14 17:05 bspeice

I'm having the same issue. Any news ?

christophesmet avatar May 28 '14 15:05 christophesmet

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.

Yougin avatar Sep 04 '14 20:09 Yougin

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.

cgruber avatar Sep 05 '14 08:09 cgruber

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

Yougin avatar Sep 05 '14 08:09 Yougin

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.

cgruber avatar Sep 05 '14 09:09 cgruber

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

dgandhi17 avatar Dec 02 '14 07:12 dgandhi17

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.

christophecommon avatar Jun 29 '15 18:06 christophecommon