简介
Dagger2是在编译期进行依赖注入的框架
依赖注入
依赖注入(Dependency Injection,简称DI):它指不在类中实例化其他依赖的类,而是先把以来的类实例化了,然后以参数的形式传入构造函数中。它可以通过这种形式,减少类之间的依赖,从而降低耦合性。
比如我们想在类A中获得类B的实例,通常的做法是new一个类B的对象:
public class ClassA {
public ClassA() {
ClassB classB = new ClassB();
}
}
这样ClassA和ClassB就是依赖的关系,比如这时由于需求变动,我需要更改ClassB的构造方法:
public class ClassB {
private int num;
public ClassB(int num) {
this.num = num;
}
}
那么我们就不得不去改ClassA,这就造成了耦合。
当然,你也会说,在ClassB里增加一个set方法或者空参构造不也行嘛,但是这只是一个简单的举例,实际需求前变万化,真正的解耦才是我们所不断追求的。
总之,我们的目标就是无论ClassB如何变动,我们都不用修改ClassA
常见的依赖注入方式
1.接口注入
interface ClassBInterface {
void setB(ClassB b);
}
public class ClassA implements ClassBInterface {
ClassB classB;
@override
void setB(ClassB b) {
classB = b;
}
}
2.构造方法注入
public class ClassA {
ClassB classB;
public void ClassA(ClassB b) {
classB = b;
}
}
3.set方法注入
public class ClassA {
ClassB classB;
public void setClassB(ClassB b) {
classB = b;
}
}
4.工厂模式注入(将需要依赖的类的实例化交给第三方类来实现注入)
public class Factory {
public ClassB getClassB() {
return new ClassB();
}
}
public class ClassA {
ClassB classB;
public ClassA() {
Factory factory = new Factory();
ClassB classB = factory.getClassB();
}
}
5.注解注入
本质上是依赖第三方类进行依赖注入的,但其通常可以通过映射关系自动生成第三方类,进行动态注入
public class ClassA {
//注解注入需要依赖注入框架的支持,如Dagger2
@Inject
ClassB classB
}
Dagger2就是通过注解的方式进行依赖注入的
使用Dagger2
添加依赖
apply plugin: 'kotlin-kapt'
kapt {
generateStubs = true
}
...
dependencies {
...
//添加依赖
implementation 'com.google.dagger:dagger:2.11'
kapt 'com.google.dagger:dagger-compiler:2.11'
}
在kotlin中添加Dagger2的依赖和java中有所不同,重点在kapt上。
如果这里不使用kapt,则不会自动编译生成DaggerXXXComponent类
至于上方那些,就是kapt所需的配置
常用注解
@Inject
用于告诉Dagger2我们需要依赖哪个类的实例对象,如:
@Inject
lateinit var apiService:ApiService
注解注入所标识的成员,不允许使用private来修饰,而在kotlin中,声明的变量默认是私有的,这就需要我们单独处理一下,比如添加lateinit关键字或者添加@JvmField注解,如:
@Inject
@JvmField
var apiService: ApiService? = null
通过以上的声明,Dagger2就知道,我们需要它帮我们注入一个ApiService类的apiService对象
@Module
标识着用于提供依赖的类,在其中使用
@Provides注解所标识的方法,就是提供依赖的方法,如:
@Module
class LoginModule {
@Provides
fun provideApiService(): ApiService = ApiService()
}
编程习惯上,@Module注解所标识的类名,使用Module结尾;@Provides注解所标识的方法名,使用provide开头
@Component
是
@Inject 和
@Module关联起来的桥梁,它需要是一个接口或抽象类,它将从
@Module中获取依赖并注入到
@Inject中,如:
@Component(modules = arrayOf(LoginModule::class))
interface LoginComponent {
fun inject(activity: MainActivity)
}
编程习惯上,@Component
注解所标识的类名,使用Component
结尾;
@Provides注解所标识的方法名,使用
provide开头
关联的@Module要
以集合的形式明确写出,可以关联多个
@
Module,如:
@Component(modules = arrayOf(LoginModule::class,RegisterModule::class))
最简单例子
了解了以上四个常用注解,我们就在Kotlin中写一个最简单的依赖注入:
我们所依赖的类ApiService:
class ApiService {
fun login() {
Log.d("ApiService", "调用login")
}
}
@Module 提供所需依赖
@Module
class LoginModule {
@Provides
fun provideApiService(): ApiService = ApiService()
}
@Component 建立@Inject 和 @Module 的关联
@Component(modules = arrayOf(LoginModule::class))
interface LoginComponent {
fun inject(activity: MainActivity)
}
@Inject 声明所需依赖,并通过编译后生成DaggerXXXComponet类,完成依赖注入:
class MainActivity : AppCompatActivity() {
//注入的成员不可以是私有的,而kotlin声明变量时,默认是私有的,所以我们需要特殊处理一下
// @Inject
// @JvmField
// var apiService: ApiService? = null
@Inject
lateinit var apiService:ApiService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//按下Ctrol+F9 build项目,Dagger2会自动生成DaggerXXXComponet类
DaggerLoginComponent.create().inject(this)
apiService.login()
}
}
参考: