Reputation: 3997
Suppose I have the following classes:
class Foo {}
class Bar {
Foo mFoo = new Foo();
}
class MyActivity extends AppCompatActivity {
Bar mBar = new Bar();
@Override
protected void onCreate (Bundle savedInstance) {
super(savedInstance);
}
}
From the code above it can be seen that MyActivity depends on Bar and Bar depends on Foo. What is the correct way to implement dependency injection using Dagger2?
So far, the way I've been dealing with this type os scenario was:
Configuration
@Singleton
@Component(
modules = {
MainModule.class,
})
public interface MainComponent {
void inject(MyActivity myActivity);
void inject(Bar bar);
}
@Module
class MainModule {
@Provides
@Singleton
Foo provideFoo() {
return new Foo();
}
@Provides
@Singleton
Bar provideBar() {
return new Bar();
}
}
Injection
class Bar {
@Inject Foo mFoo;
Bar() {inject(this);}
}
class MyActivity extends AppCompatActivity {
@Inject Bar mBar;
@Override
protected void onCreate (Bundle savedInstance) {
super(savedInstance);
inject(this);
}
}
Problem is, according to this answer, it is not supposed to work like that, Bar
should not call inject
and have Foo
passed as parameter. But, what if I need singletons instances of Foo
in other parts of my project? How should I use Dagger2 to achieve the given scenario?
Upvotes: 1
Views: 114
Reputation: 3441
You do not need to use a module to provide plain classes. You can annotate the classes directly. To always provide Foo as a singleton, you can annotate the class itself.
@Singleton
@Component
public interface MainComponent {
void inject(MyActivity myActivity);
}
@Singleton
public class Foo {
@Inject
public Foo(){}
}
@Singleton
public class Bar {
private final Foo foo;
@Inject
public Bar(final Foo foo){
this.foo = foo;
}
}
Modules are required when you need to build/configure instances or provide an implementation of an interface. For example:
@Module
class MainModule {
@Singleton
@Provides
Gson gson() {
return new GsonBuilder.create();
}
@Singleton
@Provides
IBar bar(Bar bar){
return bar;
}
}
// You can also provide an interface with the @Binds attribute instead
// of using the traditional @Provides annotation
@Module
abstract class MainBindsModule {
@Singleton
@Binds
abstract IBar bar(Bar bar);
}
Upvotes: 1
Reputation: 21437
If you change the signature of the constructor of Bar
to the following you should achieve the effect that you want (constructor injection).
class Bar {
Foo foo;
@Inject
Bar(Foo foo) {
this.foo = foo;
}
}
Then you just need to specify the dependency of Bar
in the provider by writing it as a method parameter like this:
@Provides Bar provideBar(Foo foo)
Since Foo
and Bar
are known to dagger through the @Provide
methods, dagger will automagically create an instance of Foo
to obtain an instance of Bar
. Constructor injection like this will have the added advantage of making your code easy to unit test. In your test, you can swap out a real Foo
with a mock Foo
and test the interaction of Foo
on Bar
.
Property injection is best only when you don't have access to the constructor (like in Android Activity
and Service
). For your other dependencies, it's probably best to use constructor injection.
Upvotes: 1