Dagger2
Dagger2
@Inject
Declaring Dependencies
-
Use
@Inject
to annotate the constructor that Dagger should use to create instances of a class. When a new instance is requested, Dagger will obtain the required parameters values and invoke this constructor.class Thermosiphon implements Pump { private final Heater heater; @Inject Thermosiphon(Heater heater) { this.heater = heater; } ... }
-
Dagger can inject fields directly. In this example it obtains a
Heater
instance for theheater
field and aPump
instance for thepump
field.class CoffeeMaker { @Inject Heater heater; @Inject Pump pump; ... }
-
If your class has
@Inject
-annotated fields but no@Inject
-annotated constructor, Dagger will inject those fields if requested, but will not create new instances. Add a no-argument constructor with the@Inject
annotation to indicate that Dagger may create instances as well.Dagger also supports method injection, though constructor or field injection are typically preferred.
Classes that lack
@Inject
annotations cannot be constructed by Dagger.
Satisfying Dependencies(满足依赖)
By default, Dagger satisfies each dependency by constructing an instance of the requested type as described above. When you request a CoffeeMaker
, it’ll obtain one by calling new CoffeeMaker()
and setting its injectable fields.
But @Inject
doesn’t work everywhere:
- Interfaces can’t be constructed.(接口)
- Third-party classes can’t be annotated.(第三方库)
- Configurable objects must be configured!(可配置的对象?)
@Provide
For these cases where @Inject
is insufficient or awkward, use an @Provides
-annotated method to satisfy a dependency. The method’s return type defines which dependency it satisfies.
For example, provideHeater()
is invoked whenever a Heater
is required:
@Provides static Heater provideHeater() {
return new ElectricHeater();
}
It’s possible for @Provides
methods to have dependencies of their own. This one returns a Thermosiphon
whenever a Pump
is required:(自己也可以通过@Provides对外提供实例)
@Provides static Pump providePump(Thermosiphon pump) {
return pump;
}
@Module
All @Provides
methods must belong to a module. These are just classes that have an @Module
annotation.
@Module
class DripCoffeeModule {
@Provides static Heater provideHeater() {
return new ElectricHeater();
}
@Provides static Pump providePump(Thermosiphon pump) {
return pump;
}
}
By convention, @Provides
methods are named with a provide
prefix and module classes are named with a Module
suffix.
@Component
The @Inject
and @Provides
-annotated classes form a graph of objects, linked by their dependencies. Calling code like an application’s main
method or an Android Application
accesses that graph via a well-defined set of roots.
In Dagger 2, that set is defined by an interface with methods that have no arguments and return the desired type. By applying the @Component
annotation to such an interface and passing the module types to the modules
parameter, Dagger 2 then fully generates an implementation of that contract.
@Component(modules = DripCoffeeModule.class)
interface CoffeeShop {
CoffeeMaker maker();
}
The implementation has the same name as the interface prefixed with Dagger
. Obtain an instance by invoking the builder()
method on that implementation and use the returned builder to set dependencies and build()
a new instance.
CoffeeShop coffeeShop = DaggerCoffeeShop.builder()
.dripCoffeeModule(new DripCoffeeModule())
.build();
Note: If your @Component
is not a top-level type, the generated component’s name will be include its enclosing types’ names, joined with an underscore. For example, this code:
class Foo {
static class Bar {
@Component
interface BazComponent {}
}
}
would generate a component named DaggerFoo_Bar_BazComponent
.
Any module with an accessible default constructor can be elided as the builder will construct an instance automatically if none is set. And for any module whose @Provides
methods are all static, the implementation doesn’t need an instance at all. If all dependencies can be constructed without the user creating a dependency instance, then the generated implementation will also have a create()
method that can be used to get a new instance without having to deal with the builder.
CoffeeShop coffeeShop = DaggerCoffeeShop.create();
//create方法等价于Builder().build()
create() {
return new Builder().build();
}
Now, our CoffeeApp
can simply use the Dagger-generated implementation of CoffeeShop
to get a fully-injected CoffeeMaker
.
public class CoffeeApp {
public static void main(String[] args) {
CoffeeShop coffeeShop = DaggerCoffeeShop.create();
coffeeShop.maker().brew();
}
}
@Singletons
保证在一个Component是单例,多个Component中还是多个对象,注意Component也需要用Singletons声明。
//实现层面使用了DoubleCheck将Module进行装饰
@Module
public class FoodModule {
@Singleton
@Provides
Chopsticks provideChopsticks() {
return new Chopsticks();
}
}
@Singleton
@Component(modules = FoodModule.class)
public interface FoodComponent {
Person injectPerson();
}
//返回的是一个DoubleCheck对象
this.provideChopsticksProvider =
DoubleCheck.provider(FoodModule_ProvideChopsticksFactory.create(builder.foodModule));
//获取实例,走的是DoubleCheck的get方法
instance.chopsticks = chopsticksAndChopsticks2Provider.get();
DoubleCheck
public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {
private static final Object UNINITIALIZED = new Object();
private volatile Provider<T> provider;
private volatile Object instance = UNINITIALIZED;
private DoubleCheck(Provider<T> provider) {
assert provider != null;
this.provider = provider;
}
@SuppressWarnings("unchecked") // cast only happens when result comes from the provider
@Override
public T get() {
Object result = instance;
if (result == UNINITIALIZED) {
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
result = provider.get();
/* Get the current instance and test to see if the call to provider.get() has resulted
* in a recursive call. If it returns the same instance, we'll allow it, but if the
* instances differ, throw. */
Object currentInstance = instance;
if (currentInstance != UNINITIALIZED && currentInstance != result) {
throw new IllegalStateException("Scoped provider was invoked recursively returning "
+ "different results: " + currentInstance + " & " + result + ". This is likely "
+ "due to a circular dependency.");
}
instance = result;
/* Null out the reference to the provider. We are never going to need it again, so we
* can make it eligible for GC. */
provider = null;
}
}
}
return (T) result;
}
/** Returns a {@link Provider} that caches the value from the given delegate provider. */
public static <T> Provider<T> provider(Provider<T> delegate) {
checkNotNull(delegate);
if (delegate instanceof DoubleCheck) {
/* This should be a rare case, but if we have a scoped @Binds that delegates to a scoped
* binding, we shouldn't cache the value again. */
return delegate;
}
return new DoubleCheck<T>(delegate);
}
}
@Scope
Dagger2的Scope,除了Singleton(root),其他都是自定义的,无论你给它命名PerActivity、PerFragment,其实都只是一个命名而已,真正起作用的是inject的位置,以及dependency。
Scope起的更多是一个限制作用,比如不同层级的Component需要有不同的scope,注入PerActivity scope的component后activity就不能通过@Inject去获得SingleTon的实例,需要从application去暴露接口获得(getAppliationComponent获得component实例然后访问,比如全局的navigator)。
当然,另一方面则是可读性和方便理解,通过scope的不同很容易能辨明2个实例的作用域的区别。
@Qualifier
用于不同的构造方法标识,替代@Name
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface FoodForDefault {
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface FoodForDefault {
}
@Module
public class FoodModule {
@FoodForDefault
@Provides
Food provideFood() {
return new Food();
}
@FoodForSomeThing
@Provides
Food provideFoodForSomeThing() {
//不支持带参数,如果我需要的参数是可变的,如何操作?
return new Food("哈哈哈");
}
}
public class Person {
@FoodForDefault
@Inject
Food food;
@FoodForSomeThing
@Inject
Food food1;
}
在实现层面上@Qualifier标示后就再生成了一个Factory用于构造对象
Other
依赖注入的过程
步骤1:查找Module中是否存在创建该类的方法。
步骤2:若存在创建类方法,查看该方法是否存在参数
步骤2.1:若存在参数,则按从**步骤1**开始依次初始化每个参数
步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
步骤3:若不存在创建类方法,则查找Inject注解的构造函数,
看构造函数是否存在参数
步骤3.1:若存在参数,则从**步骤1**开始依次初始化每个参数
步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
硬初始化or硬编码
public class CoffeeMachine {
private CoffeeMaker maker;
public CoffeeMachine(Cooker cooker){
maker = new SimpleMaker(cooker);
}
public String makeCoffee(){
return maker.makeCoffee();
}
}
解决上述问题的方式
public class CoffeeMachinWithInjection implements InjectMaker{
private CoffeeMaker maker;
/*依赖注入的3种常见形式
*No.1 构造函数注入
*/
public CoffeeMachinWithInjection(CoffeeMaker maker){
this.maker = maker;
}
//No.2 Setter注入
public void setMaker(CoffeeMaker maker){
this.maker = maker;
}
// //No.3 接口注入
@Override
public void injectMaker(CoffeeMaker maker) {
this.maker = maker;
}
public String makeCoffee(){
return maker.makeCoffee();
}
}
参考
http://blog.zhaiyifan.cn/2016/03/27/android-new-project-from-0-p4/