Dagger2 在 Kotlin 中使用

依赖注入想必大家耳熟能详了,Dagger 为了解耦应用而生,下面简单介绍下 Dagger2 在 Kotlin 中的使用

基本依赖

我们知道车由发动机发动机、车座、轮子等部件组成,也就是说当我们创造一辆车时,我们需要依赖发动机、轮子等部件,用代码来表示就是这样

发动机

1
2
3
4
5
class Engine {
init {
println("new Engine")
}
}

车座

1
2
3
4
5
class Seat {
init {
println("new Seat")
}
}

轮子

1
2
3
4
5
class Wheel {
init {
println("new Wheel")
}
}

1
2
3
4
5
6
7
8
9
10
11
12
class Car {
var engine: Engine? = null
var seat: Seat? = null
var wheel: Wheel? = null

init {
engine = Engine()
seat = Seat()
wheel = Wheel()
println("new Car")
}
}

当我们 new 一辆 Car 时,会输出

1
2
3
4
new Engine
new Seat
new Wheel
new Car

以上就是最基本的依赖,而写法也是按照 Java 的写法,Car 所需的依赖在初始化中初始化

按照 Kotlin 的写法应该是这样的

1
2
3
4
5
6
7
8
9
class Car(
val engine: Engine = Engine(),
val seat: Seat = Seat(),
val wheel: Wheel = Wheel()
) {
init {
println("new Car")
}
}

这样就是 Kotlin 的在构造函数中初始化的方式

不管是怎么写,反正就是为了说明最基本的依赖,而明白之后,我们自然能够看出这样写具有很强的耦合性,为了解耦,我们来看看 Dagger2 怎么弄

Dagger2 依赖注入

首先在 build.gradle 中配置

1
apply plugin: 'kotlin-kapt'

引入 Dagger2

1
2
3
4
5
dependencies {
...
kapt 'com.google.dagger:dagger-compiler:2.16'
implementation 'com.google.dagger:dagger:2.16'
}

现在开始修改代码

发动机

1
2
3
4
5
class Engine @Inject constructor() {
init {
println("new Engine")
}
}

车座

1
2
3
4
5
class Seat @Inject constructor(){
init {
println("new Seat")
}
}

轮子

1
2
3
4
5
class Wheel @Inject constructor() {
init {
println("new Wheel")
}
}

1
2
3
4
5
6
7
8
9
10
11
12
class Car {
@Inject
lateinit var engine: Engine
@Inject
lateinit var seat: Seat
@Inject
lateinit var wheel: Wheel

init {
println("new Car")
}
}

注意,车这里不能再用构造函数中初始化的方式了,因为那种方式转换成 class 时字段是用 private 修饰的,所以得用这种变量的方式

然后还需要定义一个中间人

1
2
3
4
@Component
interface CarComponent {
fun inject(car: Car)
}

Rebuild 一下,Dagger 会为我们自动生成一个 DaggerCarComponent 类,然后再修改 Car 中代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Car {
@Inject
lateinit var engine: Engine
@Inject
lateinit var seat: Seat
@Inject
lateinit var wheel: Wheel

init {
DaggerCarComponent.builder()
.build()
.inject(this)
println("new Car")
}
}

以后就是最简单的 Dagger 写法,给依赖实现解耦,运行看下效果

1
2
3
4
new Engine
new Seat
new Wheel
new Car

嗯,跟基本依赖一毛一样的

Dagger2 的进阶

上面说了是最简单的写法,在实际使用中各种依赖要复杂得多,比如那些单例的类,或者多个构造函数的,这时候该咋整呢,@Module 注解 和 @Provides 注解就登场了,还是以车举例

发动机

1
2
3
4
5
class Engine {
init {
println("new Engine")
}
}

车座

1
2
3
4
5
class Seat {
init {
println("new Seat")
}
}

轮子

1
2
3
4
5
class Wheel {
init {
println("new Wheel")
}
}

你没看错,我把这几个类又还原了

那车呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Car {
@Inject
lateinit var engine: Engine
@Inject
lateinit var seat: Seat
@Inject
lateinit var wheel: Wheel

init {
DaggerCarComponent.builder()
.carModule(CarModule()) //这个类用于提供依赖
.build()
.inject(this)
println("new Car")
}
}

这时候我们需要新建一个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Module
class CarModule {
@Provides
fun provideEngine(): Engine {
return Engine()
}

@Provides
fun provideSeat(): Seat {
return Seat()
}

@Provides
fun provideWheel(): Wheel {
return Wheel()
}
}

这个类就是用来提供依赖的

然后修改中间人

1
2
3
4
@Component(modules = [(CarModule::class)])
interface CarComponent {
fun inject(car: Car)
}

这里给 @Component 注解设置值,表示如果找不到依赖的话,将到 modules 中去寻找提供的依赖

运行看下效果

1
2
3
4
new Engine
new Seat
new Wheel
new Car

嗯,还是一毛一样的

在此告一段落,Dagger2 有其好处也有其不好的地方,在项目中酌情使用,还有更多高级用法,自己探索