Dagger的背景
Dagger最初是由Square开源的一款依赖注入框架,后由Google接管维护,发布了Dagger2。Dagger通过在编译阶段自动生成依赖注入的模板代码,帮助我们方便、快速的实现依赖注入。 Repository: github.com/google/dagg…
Dagger具有以下优势:
- 在编译阶段自动构建依赖关系图,并验证依赖关系的正确性:每个对象的依赖关系都可以得到满足;不存在循环依赖。
- 为依赖关系中的依赖项提供创建它们实例的
Factory。 - 可以指定依赖项的实例的作用范围:复用实例or每次都创建新的实例。
- 可以为同一流程所需的依赖项构建管理容器,从而保证及时释放不再需要的对象。
劣势:
- 编译期的静态依赖注入框架,会出现一些很难排查的错误。
- 上手难度较高。
- 增加编译时间,生成大量的类,增大包体积。
Dagger的基本使用
引入依赖
本文提供kotlin版本的引入方式,Java版本自行参考网上资料替换成apt即可。
apply plugin: 'kotlin-kapt'
dependencies {
implementation 'com.google.dagger:dagger:2.x'
kapt 'com.google.dagger:dagger-compiler:2.x'
}
⚠️ 部分kotlin版本和某些dagger部分版本存在不兼容情况,注意排查。
提供依赖
使用@Inject注解标记依赖项类型的构造方法,即可将该类型纳入dagger的管理中。当我们需要请求该类的实例时,Dagger将自动调用这个构造函数创建出实例。
class Student @Inject constructor() { }
Component - 依赖注入器
知道了如何创建依赖项以后,现在我们需要知道如何访问依赖。Dagger Component就是访问依赖项的入口,它也被称为依赖注入器 Injector。
Component的定义很简单,只需要编写一个接口,然后为其添加@Component注解即可。
@Component
interface CommonComponent
定义了Component后,我们需要从中获取到我们想要的依赖项,Dagger提供了两种方式来获取:
手动获取依赖项
要手动获取到依赖项,我们需要在Component中定义一个返回依赖项的接口方法:
@Component
interface CommonComponent {
fun student(): Student
}
项目编译后,Dagger就会自动生成CommonComponent的实现类:DaggerCommonComponent,然后我们就可以像使用一个服务定位器那样使用它:
val commonComponent = DaggerCommonComponent.create()
val student = commonComponent.student()
若仅止于此,Dagger的使用还算不上方便,因此,下面介绍如何使用Dagger自动注入依赖项。
自动注入依赖
要让Dagger自动注入依赖,首先得知道哪些地方需要注入依赖,Dagger使用@Inject注解标记需要注入的字段,需要注意的是,要注入的字段不能是private的。
class MainActivity : AppCompatActivity() {
@Inject
lateinit var student: Student
...
}
然后我们需要在Component接口中添加一个注入函数,该函数的参数为接收依赖项的客户端对象。
@Component
interface CommonComponent {
fun inject(activity: MainActivity)
}
编译完成后,即可在依赖容器上调用此函数为当前对象注入依赖。
class MainActivity : AppCompatActivity() {
@Inject
lateinit var student: Student
override fun onCreate(savedInstanceState: Bundle?) {
DaggerCommonComponent.create().inject(this)
Log.d("MainActivity", student.name)
}
}
通过Provides提供依赖项
@Inject注解提供依赖项在以下场合不太适用:
- 构造接口的实例。
- 构造第三方类的实例。
- 需要对实例进行配置,如设置外界参数等。
此时可以使用@Provides注解提供依赖,@Provides注解标记提供实例的方法,当请求该实例时,Dagger将自动调用该方法获取实例。@Provides方法需要放在模块中,模块是一个由@Module标记的类。
@Module
class CommonModule(
private val context: Context
) {
@Provides
fun provideSharedPreferences(): SharedPreferences {
// 这里创建了第三方类型的实例
return context.getSharedPreferences("SP_FILE", Context.MODE_PRIVATE)
}
}
使用Module时,首先需要将其添加到Component中:
@Component(modules = [CommonModule::class])
interface CommonComponent {
fun inject(activity: MainActivity)
}
然后获取容器实例执行注入也有一些变化:
DaggerCommonComponent.builder()
.commonModule(CommonModule(this))
.build()
.inject(this)
如果Module有公开的无参构造函数,在获取依赖容器时,可以不用显示的向其中注入对应的Module对象,Dagger会自动调用Module的无参构造函数,生成默认的Module对象。
当Component中所有的Module都不需要依赖外界参数而只通过Dagger默认构建时,Dagger会为Component生成一个名为create的静态工厂方法,用于更加便捷的获取Component实例。
public static CommonComponent create() {
return new Builder().build();
}
依赖传递
在创建依赖项时,我们可以在提供依赖项的provides-method和inject-constructor中传入Dagger已知的依赖项,Dagger会自动的查找并创建这些所需的依赖项注入到其中。
class Engine @Inject constructor()
// 在构造car时会自动的构造Engine的实例传入到其中
class Car @Inject constructor(val engine: Engine)