什么是 Dagger2
Dagger2
的核心是 依赖注入,对于 依赖注入 的理解,每个人都有一个自己的版本,我的理解是:通过注入的方式获得想要的依赖。
在开始学习使用 Dagger2
之前,我们最好先理解清楚这个框架帮我们做了什么,我在一开始使用 Dagger2
的时候也是这样,一上来不管三七二十一先看怎么用,结果导致的结果是用的过程中一脸问号。原因就是没有理解这个框架它究竟是帮我们干了什么。
Dagger2
是一个能帮助我们通过注解的方式,快速完成依赖注入的优秀框架。
不过也别过于神化 Dagger2
,要想使用好它,我们还是要遵循不少约定规则的,该写的代码也逃避不了。
至于 Dagger2
好处,我觉得最大的好处就是解藕了,最直观的一个解释是:哪天代码升级,需要修改一个在整个项目中大量被 new
的对象的构造方法时,如果用了 Dagger2
依赖注入,就可以不用大范围的修改代码,只要修改依赖提供者 Module
里面的 @Provides
注解的方法即可。当然上面说的都是有前提条件的,所以不必较真于此,毕竟我们的目的是理解并使用 Dagger2
。
下面先逐个击破 Dagger2
的注解。Let‘s go...
Dagger2 的注解
@Inject
@Inject
注解会在两个地方被看到:
- 依赖的构造方法
- 依赖需求者的属性或者方法
先看代码:
// 车轮
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
的起到桥梁的作用,连接 依赖需求者
和 依赖提供者
,后面会有更多讲解。
DaggerBikeComponent
是Dagger2
自动帮我们生成的,名称的规则是Dagger{ Component 名称 }
@Inject 的一些约定:
-
@Inject
不能使用private
,打开 build 后生成的代码我们会找到原因,这里留给大家自己验证; -
@Inject
修饰依赖构造方法的时候,如果存在多个构造方法,只能选择修饰一个。至于要使用多个构造方法,后面会有解决方案「???????」。
@Module & @Provides
可以看到,上面我们为了获取 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
@Component
注解的类可以理解为一个桥梁,这个桥梁的作用是连接 Module
(依赖管理员)和依赖需求者(比如依赖了 Wheel
的 Bike
),没有这个中介,@Inject
,@Module
都会失去作用。
Component
会声明一个 Module
集合,也就是说,一个 Componet
可以有多个依赖管理员。
@Qualifier & @Named
@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
这边就不绕弯子了,@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
的复用主要有下面两种方式:Component
中的 dependencies
属性和 @Subcomponent
注解
通过 dependencies 属性指定依赖的 Component
@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
中想要依赖的。
@Subcomponent
@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
就有点粗暴了,直接自己跑到人家家里各种拿,想拿啥拿啥。
网友评论