Dagger2
的核心是 依赖注入,对于 依赖注入 的理解,每一个人都有一个本身的版本,个人理解是:经过注入的方式得到想要的依赖。java
在开始学习使用 Dagger2
以前,咱们最好先理解清楚这个框架帮咱们作了什么,我在一开始使用 Dagger2
的时候也是这样,一上来无论三七二十一先看怎么用,结果致使的结果是用的过程当中一脸问号。缘由就是没有理解这个框架它到底是帮咱们干了什么。git
Dagger2
是一个能帮助咱们经过注解的方式,快速完成依赖注入的优秀框架。程序员
不过也别过于神化 Dagger2
,要想使用好它,咱们仍是要遵循很多约定规则的,该写的代码也逃避不了。github
至于 Dagger2
好处,我以为最大的好处就是解藕了,最直观的一个解释是:哪天代码升级,须要修改一个在整个项目中大量被 new
的对象的构造方法时,若是用了 Dagger2
依赖注入,就能够不用大范围的修改代码,只要修改依赖提供者 Module
里面的 @Provides
注解的方法便可。固然上面说的都是有前提条件的,因此没必要较真于此,毕竟咱们的目的是理解并使用 Dagger2
。bash
下面先逐个击破 Dagger2
的注解。Let‘s go...app
@Inject
注解会在两个地方被看到:框架
先看代码:ide
// 车轮
public class Wheel {
@Inject
public Wheel(){
Log.e("Wheel", "我是自行车轮胎");
}
}
// 自行车
public class Bike {
// 方式一
@Inject
Wheel mWheel;
// 方式二
//@Inject
//public void setWheel(Wheel wheel){
// this.mWheel = wheel;
//}
public Bike() {
Log.e("Bike", "我是自行车");
DaggerBikeComponent.builder().build().inject(this);
}
}
// 自行车依赖注入器
@Component
public interface BikeComponent {
// 这个方法名能够任意起,只是你们更习惯用 inject
// 由于inject 比较形象
void inject(Bike bike);
}
复制代码
自行车对象依赖车轮,咱们使用 @Inject
注解了 Wheel
的构造方法,以及 Bike
中的 mWheel
属性(或者被注释的方法),结合被 @Component
注解的 BikeComponent
,咱们实现了最最简单的依赖注入代码。学习
DaggerBikeComponent
的起到桥梁的做用,链接 依赖需求者
和 依赖提供者
,后面会有更多讲解。ui
DaggerBikeComponent
是Dagger2
自动帮咱们生成的,名称的规则是Dagger{ Component 名称 }
@Inject
不能使用 private
,打开 build 后生成的代码咱们会找到缘由,这里留给你们本身验证;@Inject
修饰依赖构造方法的时候,若是存在多个构造方法,只能选择修饰一个。至于要使用多个构造方法,后面会有解决方案「???????」。能够看到,上面咱们为了获取 Wheel
依赖,是直接修改了 Wheel
的源码,在其构造方法上动了些手脚,加上了 @Inject
注解。可是若是咱们使用的是一个第三方库提供的类的话,@Inject
这招就行不通了。
@Module
登场!!!
先看代码:
// 【新加】车灯,没有被 @Inject 注解构造方法
// 三方提供
public class Light {
public Light(){
Log.e("Light", "我是车灯");
}
}
// 自行车
public class Bike {
@Inject
Wheel mWheel;
@Inject
Light mLight;
//@Inject
//public void setWheel(Wheel wheel){
// this.mWheel = wheel;
//}
public Bike() {
Log.e("Bike", "我是自行车");
// 【修改1】多了 .bikeModule(new BikeModule())
DaggerBikeComponent.builder().bikeModule(new BikeModule()).build().inject(this);
}
}
// 自行车依赖注入器 【修改2】指定了 modules 为 BikeModule
@Component(modules = {BikeModule.class})
public interface BikeComponent {
void inject(Bike bike);
}
// 【新加】自行车依赖管理员
@Module
public class BikeModule {
// 名字没有规定,
// 通常为 provide+待被提供的依赖名称
@Provides
Light provideLight(){
return new Light();
}
}
复制代码
能够将 @Module
注解的类理解为依赖管理员,这个管理员经过 @Provides
注解的方法提供依赖给依赖需求者,好比上面的例子,经过 @Provides
注解了 Light provideLight()
方法,该方法实际直接返回了一个 Light
对象( 这里就是我一开始讲的,该写的代码仍是逃避不了,好比建立这个依赖的代码仍是须要咱们手动的血出来 )。
Dagger2
在寻找依赖的时候有一个顺序,会先从@Module
中寻找,若是找不到再从@Inject
注解了构造方法的类寻找,这个了解一下就好,在定位一些问题的时候会派上用场
@Component
注解的类能够理解为一个桥梁,这个桥梁的做用是链接 Module
(依赖管理员)和依赖需求者(好比依赖了 Wheel
的 Bike
),没有这个中介,@Inject
,@Module
都会失去做用。
Component
会声明一个 Module
集合,也就是说,一个 Componet
能够有多个依赖管理员。
@Qualifier
注解的做用按字面理解便知一二,Qualifier
是 “限定” 的意思,意思能够用它来限定一些东西,具体限定什么,先看下面的例子,Light
类多个一个灯色的属性:color
, 默认颜色是白色。
public class Light {
private String color = "white";
public Light() {
Log.e("Light", "我是车灯, 而且颜色是:" + this.color);
}
public Light(String color) {
this.color = color;
Log.e("Light", "我是车灯, 而且颜色是:" + this.color);
}
}
复制代码
如今咱们的 Light
类有两个构造方法,改写一下依赖管理员:
@Module
public class BikeModule {
@Provides
Light provideLight() {
return new Light();
}
@Provides
Light provideRedLight() {
return new Light("red");
}
}
复制代码
这个时候咱们 run build
一下,会获得下面的编译错误:
xxx.xx.xxx.Light is bound multiple times...
复制代码
说明直接这样写确定不行,那么能够请 @Qualifier
登场了。首先咱们自定一个 Qualifier
:
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface LightColorQualifier {
String value() default "white";
}
复制代码
下面调整一下 Module
和 @Inject
的地方就可让代码又从新跑起来了
@Module
public class BikeModule {
@LightColorQualifier()
@Provides
Light provideLight() {
return new Light();
}
@LightColorQualifier("red")
@Provides
Light provideRedLight() {
return new Light("red");
}
}
public class Bike {
@Inject
Wheel mWheel;
@LightColorQualifier("red")
@Inject
Light mLight;
//@Inject
//public void setWheel(Wheel wheel){
// this.mWheel = wheel;
//}
public Bike() {
Log.e("Bike", "我是自行车");
DaggerBikeComponent.builder().bikeModule(new BikeModule()).build().inject(this);
}
}
复制代码
你能够试着改变 @LightColorQualifier("red")
为 @LightColorQualifier("blue")
, 会发现 build 又会失败,这说明这个 @LightColorQualifier("red")
起到了一个限定的做用,依赖管理员会根据这个限定去拿须要的依赖给需求者。
讲完 @Qualifier
的原理和做用,@Named
水到渠成,看源码可知:
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
/** The name. */
String value() default "";
}
复制代码
是的,@Named
只是一个官方帮咱们实现好的 @Qualifier
,没啥特殊要求,直接用这个便可,至于在定义本身的 @Qualifier
的时候,下面几个方面能够你们本身尝试探究一下:
@Named
用的 value()
,能否换成其余的名字?String
以外,还能够用其余类型吗,好比 int
,boolean
之类?这边就不绕弯子了,@Scope
和 @Singleton
的关系,其实和上面的 @Qualifier
,@Named
同样, @Singleton
是一个官方 @Scope
实现:
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
复制代码
回到 Scope
,中文意思是 “范围”,使用 @Scope
能够约定一些声明周期,好比咱们开发中常常会用到 @ActivityScope
,@FragmentScope
,可是是否是咱们只要使用了 @Singleton
,@ActivityScope
,@FragmentScope
。。依赖就会自动有了生命周期了呢?
固然不会,仍是开头讲的,不能神化 Dagger2
。。
@Singleton
并非真正意义上用来建立单列的。 其实 Dagger2
真正建立单列的流程是:
Module
实现全局类的实现AppComponent
中使用这个 Module
AppComponent
只会初始化一次,通常会在 Application
的 onCreate()
方法中建立这个 AppComponent
那么 @Singleton
的做用是什么:
Module
和 Component
之间的关系,保证 AppComponent
和 Module
是匹配的。若AppComponent
和 Module
的 Scope
是不同的,则会在编译时报错。Module
中建立的类实例是单例。最后整理一些约定:
@Provides
方法注解了 Scope
,@Component
必定要有注解@Component
注解了 Scope
,不必定非得有构造方法或者 @Provides
方法被 Scope
注解@Scope
的 @Component
能够依赖有 @Scope
的 @Component
,有 @Scope
的 @Component
只能依赖有 @Scope
的 @Component
,而且二者的 @Scope
不能相同@Singleton
的 @Component
只能被依赖而不能依赖任何 @Component
好的程序员怎能不会偷懒???
Component
的复用主要有下面两种方式:Component
中的 dependencies
属性和 @Subcomponent
注解
@ActivityScope
@Component(modules = {MainModule.class } , dependencies = {AppComponent.class})
public interface MainComponent {
void inject(MainActivity mainActivity);
}
@Singleton
@Component(modules = { AppModule.class })
public interface AppComponent {
Gson gson();
}
复制代码
这种方式,若是在 MainActivity
中想要获取到 AppModule
里面提供的依赖,必须在 AppComponent
里面显式的提供出来。否则的话是无法在 MainActivity
中获取到 AppModule
中想要依赖的。
@ActivityScope
@Subcomponent(modules = {MainModule.class })
public interface MainComponent {
void inject(MainActivity mainActivity);
}
@Singleton
@Component(modules = { AppModule.class })
public interface AppComponent {
MainComponent plus(MainModule mainModule);
}
public class MainActivity extends AppCompatActivity {
@Inject Gson mGson;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DemoApp.getInstance().getAppComponent().plus(new MainModule(this)).inject(this);
//DaggerMainComponent.builder()
// .appComponent(DemoApp.getInstance().getAppComponent())
// .mainModule(new MainModule(this))
// .build()
// .inject(this);
Log.e("MainActivity", mGson.toString());
//new Bike();
}
}
复制代码
@Subcomponent
注解的 Component
不会自动生成相似 DaggerxxxComponent
的类文件,实际上是把本身扔到了父 Component
中,从而得到到父 Component
的全部注入属性, 好比上面咱们就没有再显式的在 AppComponent
中提供 Gson
对象了。
经过这两个例子,你们能够对比体会一下两者的区别。我我的的理解是 dependencies
更加委婉,我打好招呼要啥再给啥,@Subcomponent
就有点粗暴了,直接本身跑到人家家里各类拿,想拿啥拿啥。