Aleksander Mielczarek
Aleksander Mielczarek

Reputation: 2825

Dagger throws IllegalStateException while injecting nested dependency

I want to inject into Activity object that has nested dependency. All dependencies are managed by Dagger module.

I've tried to do it for three different way. For me the best is method third, which actually throws me an error, which I don't fully understand.

Can you explain me why third method isn't working?

public class Bar {
    public void bar() {
        Log.i("Bar", "bar");
    }
}

public class MainActivity extends Activity {

    @Inject
    Foo foo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ObjectGraph.create(new MainModule()).inject(this);
        ButterKnife.inject(this);
    }

    @OnClick(R.id.button)
    void onButtonClick() {
        foo.foo();
    }
}

First method is working - Foo is implementation of an interface:

public interface IFoo {
    public void foo();
}

public class Foo implements IFoo{

    @Inject
    Bar bar;

    @Override
    public void foo() {
        bar.bar();
    }
}

@Module(injects = MainActivity.class, library = true)
public class MainModule {

    @Provides
    @Singleton
    Bar provideBar() {
        return new Bar();
    }

    @Provides
    @Singleton
    IFoo provideFoo(Foo foo) {
        return foo;
    }

}

Second method is working - Foo is normal class but module doesn't contains it:

public class Foo {

    @Inject
    Bar bar;

    public void foo() {
        bar.bar();
    }
}

@Module(injects = MainActivity.class)
public class MainModule {

    @Provides
    @Singleton
    Bar provideBar() {
        return new Bar();
    }

}

Third method throws an error - Foo is normal class and module contains it:

Error:(12, 8) error: Unknown error java.lang.IllegalStateException thrown by javac in graph validation: Dependency cycle:
0. foo.Foo bound by @Singleton/ProvidesBinding[key=foo.Foo method=foo.MainModule.provideFoo()
0. foo.Foo

public class Foo {

    @Inject
    Bar bar;

    public void foo() {
        bar.bar();
    }
}

@Module(injects = MainActivity.class, library = true)
public class MainModule {

    @Provides
    @Singleton
    Bar provideBar() {
        return new Bar();
    }

    @Provides
    @Singleton
    Foo provideFoo(Foo foo) {
        return foo;
    }

}

Upvotes: 2

Views: 1621

Answers (1)

Egor
Egor

Reputation: 40193

This provider method creates a cycle:

@Provides
@Singleton
Foo provideFoo(Foo foo) {
    return foo;
}

You need an instance of Foo to create an instance of Foo, so you can never obtain an instance.

Proposed solution:

public class Foo {

    private final Bar bar;

    @Inject
    public Foo(Bar bar) {
        this.bar = bar;
    }

    public void foo() {
        bar.bar();
    }
}

@Module(injects = MainActivity.class, library = true)
public class MainModule {

    @Provides
    @Singleton
    Bar provideBar() {
        return new Bar();
    }

    @Provides
    @Singleton
    Foo provideFoo(Bar bar) {
        return new Foo(bar);
    }

}

Upvotes: 3

Related Questions