Components ought to be the "big DI provider" that provides everything for a specific scope.
For example, you could have a SingletonComponent
with @Singleton
scope that has every single module added to it that has at least one @Singleton
scoped provider method.
@Singleton
@Component(modules={NetworkingModule.class, DatabaseModule.class, MapperModule.class, UtilsModule.class})
public interface SingletonComponent {
// provision methods
OkHttpClient okHttpClient();
RealmHolder realmHolder();
// etc.
}
You can have the provision methods defined per module.
public interface DatabaseComponent {
RealmHolder realmHolder();
}
public interface NetworkingComponent{
OkHttpClient okHttpClient();
}
In which case you'd have
@Singleton
@Component(modules={NetworkingModule.class, DatabaseModule.class, MapperModule.class, UtilsModule.class})
public interface SingletonComponent
extends NetworkingComponent, DatabaseComponent, MapperComponent, UtilsComponent {
// provision methods inherited
}
In a Module, you can specify a factory method ("provider method") that specifies how to create a particular type of dependency.
For example,
@Module
public class NetworkingModule {
@Provides
@Singleton
OkHttpClient okHttpClient() {
return new OkHttpClient.Builder()./*...*/.build();
}
@Provides
@Singleton
Retrofit retrofit(OkHttpClient okHttpClient) {
// ...
}
}
You can imagine the @Singleton
scope as the big DI container that Spring would have given you.
You can also provide instances of the class using @Inject
annotated constructor. This can receive any class from the component that is able to instantiate it from provider methods within that scoped component's modules (and unscoped dependencies, of course).
@Singleton
public class MyMapper {
@Inject
public MyMapper(RealmHolder realmHolder, OkHttpClient okHttpClient) { // totally random constructor for demo
}
}
or
@Singleton
public class MyMapper {
@Inject
RealmHolder realmHolder;
@Inject
OkHttpClient okHttpClient;
@Inject
public MyMapper() {
}
}
Then this will be available in the component, you can even make provision method for it to make it inheritable in component dependencies:
@Singleton
@Component(modules={...})
public interface SingletonComponent {
MyMapper myMapper();
}
With Dagger2, you can also additionally create "subscoped components", that inherit all dependencies provided from a component of a given scope.
For example, you can inherit all @Singleton
scoped components, but you can still have new scoped dependencies per that new scope, such as @ActivityScope
.
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}
Then, you can create subscoped components using either subcomponents, or component dependencies.
.
@ActivityScope
@Subcomponent(modules={MainActivityModule.class})
public interface MainActivityComponent {
MainPresenter mainPresenter();
}
Then this can be created in its parent scoped component:
@Singleton
@Component(modules={...})
public interface SingletonComponent {
MainActivityComponent mainActivityComponent(MainActivityModule module);
}
Then you can use the singleton component to instantiate this:
SingletonComponent singletonComponent = DaggerSingletonComponent.create();
MainActivityComponent mainActivityComponent = singletonComponent.mainActivityComponent(new MainActivityModule(mainActivityHolder));
.
@ActivityScope
@Component(dependencies={SingletonComponent.class}, modules={MainActivityModule.class})
public interface MainActivityComponent extends SingletonComponent {
MainPresenter mainPresenter();
}
For this to work, you must specify provision methods in the superscoped component.
Then you can instantiate this like so:
SingletonComponent singletonComponent = DaggerSingletonComponent.create();
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
.singletonComponent(singletonComponent)
.mainActivityModule(new MainActivityModule(mainActivityHolder))
.build();
In Dagger2, you can therefore obtain dependencies either via:
@Inject
annotated constructor parameters
@Inject
annotated fields on classes with @Inject
annotated constructor
- from
@Component
provision methods
- via manual field injection method defined in component (for classes you can't create using
@Inject
annotated constructor)
Manual field injection can happen for classes like MainActivity
, that you yourself don't create.
Manual field injection injects only the specific class that you are injecting. Base-classes don't get automatically injected, they need to call .inject(this)
on component.
It works like this:
@ActivityScope
@Subcomponent(modules={MainActivityModule.class})
public interface MainActivityComponent {
void inject(MainActivity mainActivity);
}
Then you can do:
public class MainActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
.singletonComponent(getSingletonComponent())
.mainActivityModule(new MainActivityModule(this))
.build(); // ensure activity `holder` instead, and retain component in retained fragment or `non-configuration instance`
mainActivityComponent.inject(this);
}
}