我们再回到之前的例子,现在我们把前面使用单例的部分都去掉,再来继续讲其他的部分。在上面的CoffeeModule
中,把每个provideXXX()
都添加一句log
:
@Module
public class CoffeeModule {
@Provides
Heater provideHeater() {
System.out.println("provide heater");
return new ElectricHeater();
}
@Provides
Pump providePump() {
System.out.println("provide pump");
return new Thermosiphon();
}
@Provides
Ice provideIce() {
System.out.println("provide ice");
return new NanjiIce();
}
@Provides
IceBox provideIceBox(Ice ice) {
System.out.println("provide ice box");
return new HaierIceBox(ice);
}
...
}
然后我们在CoffeeMaker
中,只调用Component
的inject
方法,但是不去使用该对象:
class CoffeeMaker {
@Inject
Heater heater;
@Inject
Lazy<Pumb> pump; // 使用lazy
@Inject
Provider<IceBox> iceBox; // 使用provider
@Inject
@Type("shuiguo")
Milk shuiguoMilk;
CoffeeMaker() {
CoffeeComponent component = DaggerCoffeeComponent.create();
component.inject(this);
}
public void brew() {
// heater.on();
// pump.pump();
System.out.println(" [_]P coffee! [_]P ");
// iceBox.addIce();
// shuiguoMilk.addMilk();
// heater.off();
}
}
执行结果:
04-21 15:03:11.684 17811-17811/com.charon.stplayer I/System.out: provide heater
04-21 15:03:11.694 17811-17811/com.charon.stplayer I/System.out: [_]P coffee! [_]P
可以看到普通注入只要声明就会被初始化,而使用Provider
和Lazy
包装的并没有进行初始化, 接下来我们分别对这些不同对象调用两次:
class CoffeeMaker {
@Inject
Heater heater;
@Inject
Lazy<Pump> pump;
@Inject
Provider<IceBox> iceBox;
@Inject
@Type("shuiguo")
Milk shuiguoMilk;
CoffeeMaker() {
CoffeeComponent component = DaggerCoffeeComponent.create();
component.inject(this);
}
public void brew() {
heater.on();
System.out.println(" heater on ");
heater.on();
System.out.println(" heater on ");
pump.get().pump();
System.out.println(" pump pump ");
pump.get().pump();
System.out.println(" pump pump ");
System.out.println(" [_]P coffee! [_]P ");
iceBox.get().addIce();
System.out.println(" iceBox addIce ");
iceBox.get().addIce();
System.out.println(" iceBox addIce ");
shuiguoMilk.addMilk();
heater.off();
}
}
执行结果:
provide heater // 一共执行了一次provide heater
~ ~ ~ heating ~ ~ ~
heater on
~ ~ ~ heating ~ ~ ~
heater on
provide pump // 一共执行了一次provide pump
=> => pumping => =>
pump pump
=> => pumping => =>
pump pump
[_]P coffee! [_]P
provide ice // 执行了第一次provide ice 和 provide icebox
provide ice box
加冰了,心飞扬
iceBox addIce // 调用了第一次iceBox.addIce方法
provide ice // 执行了第二次provide ice 和 provide icebox
provide ice box
加冰了,心飞扬
iceBox addIce // 调用了第二次iceBox.addIce方法
添加:caomei牛奶
可以看到使用Provider
包装的类,每次调用都会重新获取新的实例,而使用普通注入和使用Lazy
包装都使用的是用一个实例。
当然,如果限定局部单例之后,无论是Provider
还是Lazy
,在同一个Activity
中只会获取同一个依赖对象.
关于Dagger
的部分基本都讲完了,后面就是讲Dagger
和Android
的结合了。这里总结一下Dagger
的依赖注入规则是:
- 1.查找
Module
中是否存在创建该类的方法。 - 2.若存在创建类方法,查看该方法是否存在参数
- 2.1若存在参数,则按从步骤1开始依次初始化每个参数
- 2.2若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
- 3若不存在创建类方法,则查找
Inject
注解的构造函数,看构造函数是否存在参数- 3.1若存在参数,则从步骤1开始依次初始化每个参数
- 3.2若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
依赖对象的注入源应该是有两个,一是Module中的@Provides方法,而是使用@Inject注解的构造函数。从前面示例中可以看出,如果使用@Scope注解类同时使用@Inject注解构造函数时,第一次创建时调用构造函数,然后将创建的实例缓存。以后再依赖注入时,不再调用构造函数创建,而是取缓存中的实例。根据Dagger的依赖注入规则,Module中的@Provides方法的优先级高于@Inject,那么使用@Scopre使用注解@Provides方法会产生什么样的效果呢?
如果你想某个绑定在component
的某些依赖不满足的情况下也能工作,可以给Module
添加一个@BindsOptionalOf
方法:
@BindsOptionalOf abstract CoffeeCozy optionalCozy();
这就意味着@Inject
构造器和成员和@Provides
方法可以依赖一个Optional<CoffeeCozy>
对象,如果component
绑定了CoffeeCozy
那么Optional
就是当前的,否则Optional
就是缺省的。你可以注入一下几种类型:
Optional<CoffeeCozy>
Optional<Provider<CoffeeCozy>>
Optional<Lazy<CoffeeCozy>>
Optional<Provider<Lazy<CoffeeCozy>>>
如果你想在绑定component
时注入参数,如app
需要一个用户名参数,就可以给component
的builder
方法添加一个[@BindsInstance][BindsInstance]
方法注解以使用户名字符串实例可以被注入到component
中:
@Component(modules = AppModule.class)
interface AppComponent {
App app();
@Component.Builder
interface Builder {
@BindsInstance Builder userName(@UserName String userName);
AppComponent build();
}
}
public static void main(String[] args) {
if (args.length > 1) { exit(1); }
App app = DaggerAppComponent
.builder()
.userName(args[0])
.build()
.app();
app.run();
}
Dagger2
注解处理器是严格模式的,如果所有的绑定无效或者不完整就会导致编译时错误。例如这个module
被安装到component
,但是忘了绑定Executor
:
@Module
class DripCoffeeModule {
@Provides static Heater provideHeater(Executor executor) {
return new CpuHeater(executor);
}
}
那么当编译时,javac就会拒绝这个错误的绑定:
[ERROR] COMPILATION ERROR :
[ERROR] error: java.util.concurrent.Executor cannot be provided without an @Provides-annotated method.
只需要在当前component
的任一Module
中新增一个Executor
的@Provides
方法即可修复这个错误。
相信大家经常会使用@Provides
来在Module
里面提供需要注入对象的构造, 但从来没有用过@Binds
.
如果我们需要注入一个接口的实现,我们常常会这么做:
@Module
public class HomeModule {
@Provides
public HomePresenter providesHomePresenter(){
return new HomePresenterImp();
}
}
其实这样的代码可以通过@Binds
简化为:
@Module
public abstract class HomeModule {
@Binds
public abstract HomePresenter bindHomePresenter(HomePresenterImp homePresenterImp);
}
同时你需要将你的Module
改为abstract
即可,但是要注意这两个不能共存于一个Module
,是不是很简单.
- 邮箱 :charon.chui@gmail.com
- Good Luck!