-
Each
@Component
has its own bindings & Scope. -
4 most common bindings
@Inject
Constructor which is the most preferred binding@Binds
abstract method that maps a Concrete-Type to an Abstract-Type (that classed depend upon)@Provides
is generally used for mapping Concrete-Types that are third-party (or) external to Abstract-Types@BindsInstance
is generally used on a Component/SubComponent Builder method to bind a concrete type with the Component/Sub-component instance.- This is most commonly used to bind primitive types (like int, float, etc.,) to component instance.
- If more than one value of the same primitive type needs to be bound, then we would need a Qualifier annotation.
-
Provider Methods
should be preferred when we are injectingthird-party
classes- Provider Methods CAN ONLY BE defined in
Module
, and the correspondingModule
should be mapped toComponent
.
Provide Methods can be "static" if they don't depend on the state/members of a Module.
- Provider Methods CAN ONLY BE defined in
-
Binds Method
should be preferred when we want to bind an implementation to an interface dependency.@Module public abstract class AModule{ @Binds abstract Interface bindImpl(InterfaceImpl impl); @Provides Interface provideImpl(InterfaceImpl impl) { return impl; } }
bindImpl
andprovideImpl
in the above example does the exact same thing.
@Binds
is preferred in this case as we don't do anything extra with the Bound type. -
@Inject
constructors (that doesn't have any other injected params) can generally be accessible by any component.-
It is the Scope that restricts it's Binding to a "particular component".
Component scoped with "scope1" may not reference bindings with different scopes "scope2". This is the very reason, we shouldn't be using the same scope on multiple components.
-
If "another Component" has to use this dependency, then
- it has to add the "particular component" as it's
dependency
and expose a provision method in the "particular component". - or declare itself as
@Subcomponent
See Below for more details on Multiple Components + Multiple Scopes , Component Dependencies. (ex8)
- it has to add the "particular component" as it's
-
Constructor Injection
should be preferred whenever possible.Field Injection / Member Injection
should be preferred when we don't have access to the Constructors.- Downside of Field Injection is that fields / members cannot be private.
Method Injection
should be preferred when we want to do some configuration after we create an Object.
Field and Method Injection is automatic ONLY WHEN Constructor Injection is in place.
Field and Method Injection happens on an Object which doesn't have Constructor Injection, when we call the corresponding
inject
method on theComponent
with theObject-Type as parameter
.In this scenario, we pass our instance to the
Component
and ask it to inject the fields & method for us.
- Order of Injection:
- Constructor Injection.
- Field Injection.
- Method Injection.
-
We can pass runtime values to dependencies via the
Module
s that they are Provided in.- Pass the runtime value to
Module
constructor and store it as a member of the Module - Use the member variable in
@Provides
methods, while instantiating the dependency.
Please note that a
Module
with custom constructor must be passed explicitly to correspondingComponents
builder method while instantiating the component. - Pass the runtime value to
-
Bind a
component
instance with a configuration value.- Marking a method on
@Component.Builder
(or)@Component.Factory
as@BindsInstance
and pass the configuration object.@Component.Builder interface Builder { @BindsInstance Builder horsePower(int horsePower); //horsePower is the configuration object here. CarComponent build(); }
- Now each instance of the
Component
will be bound to the passed configuration value, and the same would be used on dependency injection.
- Marking a method on
- Using
@Named
annotation on the configuration parameter. (which is a@Qualifier
annotations)@Component.Builder interface Builder { @BindsInstance Builder horsePower(@Named("horsePower") int horsePower); @BindsInstance Builder engineCapacity(@Named("engineCapacity") int engineCapacity); CarComponent build(); }
- Use
@Singleton
to instantiate atype
only once (per component). - Scope a
@Component
and it's corresponding@Inject constructor enabled concrete type
(or)@Binds abstract method
(or)@Provides method
, with any Scoped annotation (here @Singleton) to instructhow many instances of the "type"
can exist per component.
-
Each
component
can have its own Scope, and the bindings can be scoped under the same. -
A
Component
can mention "another component" as dependency
meaning we will pass in the "another component" (typically using a @Component.Builder method) while creating the
Component
.
- corresponding builder gets auto-created when we don't have an explicit
@Component.Builder
. - If we have an explicit
@Component.Builder
we have to declare the builder method ourselves, else will result in an error.Error: @Component.Builder is missing setters for required modules or components.
NOTE: Please note that the dependencies of "another-component" CAN ONLY BE ACCESSED by "the-component" via PROVISION METHODS declared in "another-component" in this dependency approach.
Else we would get Error: "the-component" may not reference bindings with different scopes.
There are multiple ways in which we can connect 2 components together.
-
@Component Comp2
can mention@Component Comp1
as it's dependency.
AndComp1
has to expose provision methods for each dependency that it exposes to other components such asComp2
.@Component(dependencies = {Comp1.class}, modules = {Comp2Module1.class, Comp2Module2.class}) public interface Comp2 { }
-
@Subcomponent Comp2
can be made sub-component.-
Method1 (See ex9)
- And corresponding provision method needs to be exposed in
@Component Comp1
to create a connection.
Any Module inside sub-component
Comp2
which needs runtime values, must be passed as parameter to the provision method declared inComp1
.Comp2 implementation will become private inner class of Comp1 implementation. So any dependencies / runtime params that Comp2 need must be passed via Comp1's corresponding provision method.
interface Comp1 { Comp2 newComp2(Comp2Module2 module2Dep); }
- And corresponding provision method needs to be exposed in
-
Method1 (See ex10)
:- Explicitly declare
@Subcomponent.Builder
forComp2
. - Add provision method for the SubComponentBuilder in
@Component Comp1
.
@Component public interface Comp1 { Comp2.Builder comp2Builder(); }
- Explicitly declare
-
Method3
(TODO)
- Make
@Subcomponent Comp2
as Sub-Component of a Module@Module Comp2.InstallModule
. - Add
Comp2InstallModule
as a module of@Component Comp1
.
@Component(modules = {Comp1Module1.class, Comp2.InstallModule.class}) interface Comp1 { } @Subcomponent(modules={Comp2Module1.class}) interface Comp2 { @Module(subcomponents = {Comp2.class}) interface InstallModule{ } }
-
Component Builder | Component Factory |
---|---|
Has a series of builder methods for Module with no default constructir and dependency Component/Subcomponent . |
Has a single method that accepts all the dependency as parameter. |
If the configuration is wrong, we get exception at run-tie | Since here the dependencies are part of parameter list, we get compile time error if there is a mismatch in runtime-parameters. |
- Dagger2 Beginner Tutorial - Android
- Dagger2 - Injecting ViewModels on Android
- Dagger2 - Injecting ViewModels on Android Using FactoryProvider
- Dagger2 - Kotlin Best Practices