如果还没入门的,可以先去搜搜基本用法,本系列主要偏向原理
Dagger版本 2.11
1.Dagger2
Dagger2是啥,Google告诉我们:
Dagger is a fully static, compile-time dependency injection framework for both Java and Android. It is an adaptation of an earlier versioncreated by Square and now maintained by Google.
Dagger aims to address many of the development and performance issues that have plagued reflection-based solutions. More details can be found in this talk(slides) by +Gregory Kick.
Dagger是为Android和Java平台提供的一个完全静态的,在编译时进行依赖注入的框架,原来是由Square公司维护的然后现在把这堆东西扔给Google维护了。Dagger解决了基于反射带来的开发和性能上的问题(因为Dagger并没有用反射来做依赖注入, 靠的是注解生成代码实现依赖注入)
ps1:本文不讲使用方法
ps2:本篇实例使用的都是kotlin
2.简单用法
举个例子,Target类里有一个Member类的成员变量,也就是Target依赖Member
class Member constructor()
class Target(val member: Member)
看上去并没有什么问题,但是这并不是好的写法.
如果Target类不单单依赖Member,还依赖很多类的话,就得写很多创建实例的代码,甚至如果以后Member类需要修改代码时,那还得修改Target类的代码.如果Member类被很多类依赖的话,修改起来就更加困难.
所以dagger2就是用来完成这个实例化并赋值b = new Member();
的事情(也就是依赖注入)
dagger的注解有component\inject\module\scpoe
首先来最简单的写法,Member构造方法没有参数,Target依赖Member的情况,也就是上面例子那样的
给Member的构造函数添加@Inject注解,告诉dagger此类是可以实例化的
class Member @Inject constructor()
接着定义一个Component,也就是注入器(dagger就是根据这个接口生成实际的注入代码)
@Component
interface TargetComponent {
fun inject(target:Target)
}
然后是Target,member标记@Inject表示该变量可以被注入(从上面生成注入器的操作也能看出注入的代码不在类本身里面,所以member变量是不能标记为final或者私有的)
class Target {
@Inject
lateinit var member:Member
}
这样标注的过程就完成了,编译之后,就会生成DaggerTargetComponent(名字为Dagger+你定义的Component接口)的名称的类,在需要注入的时候调用
val target = Target()
DaggerTestComponent.builder().build().inject(target)
这样target里面的member实例就被注入进去了
3.dagger生成的代码解析
下面我们来看dagger生成的代码,找到build/generated/kapt/debug/xxx(你的包名)
可以看到生成了三个类,
- DaggerTargetComponent:表示这层依赖关系的组件
- Member_Factory:提供依赖实例的工厂
- Target_MembersInjector:就是实际用于注入依赖的注入器
下面再来看这三个类
3.1 DaggerTargetComponent
可以看到这是一个Builder模式(这里看上去简单,但依赖多了就复杂了)
这里主要看inject方法,用注入器调用injectMembers方法,传入的参数是我们需要注入Member的Target实例
跟进injectMembers(target)方法
这就是一个dagger定义的接口,那么就得看具体实现了,回到DaggerTargetComponent, 找到initialize方法,这里是注入器targetMembersInjector初始化的地方。
this.targetMembersInjector = Target_MembersInjector.create(Member_Factory.create());
先跟进Member_Factory.create()方法,来到代码生成的第二个类Member_Factory
3.2 Member_Factory
这就是一个简单的工厂类,用来提供Member实例的,我们知道Member的构造方法被标记@Inject了,加上又没有参数,所以生成的代码就直接new了
其他没啥好讲了,回到前面的
this.targetMembersInjector = Target_MembersInjector.create(Member_Factory.create());
接下来就是跟进Target_MembersInjector.create()方法了
3.3 Target_MembersInjector
这里主要就是injectMembers的实现了
instance.member = memberProvider.get();
memberProvider就是前面的工厂(Member_Factory实现了Factory<Member>,而Factory<Member>继承了Provider<Member>),提供了Member的实例
这里就能看出为啥Target的成员member不能是final(赋值在类外)和至少得是包级的权限(同包不同类)
ps:关于生成代码的包位置,基本上就是对应的类生成的包位置相同
- TargetComponent->DaggerTargetComponent
- Target->Target_MembersInjector
- Member->Member_Factory
4.依赖不能直接创建的情况
前面的例子中,Member的构造函数是无参的,那么如果有参数呢?
这里又分为两种情况:
- 参数对象无法直接创建
- 参数对象可以直接创建
4.1参数对象无法直接创建
这里就是另外一个注解Module以及Provide的作用了,顾名思义,这Module就是Target所依赖的一个模块提供者.
改写一下Member,构造函数需要一个String类型的参数
class Member @Inject constructor(val name: String)
然后创建一个MemberModule类
@Module
class MemberModule {
@Provides
fun provideMember(): Member = Member("from module provider")
}
Module表示这是模块,可以放在组件中。
Provides表示这是实例提供者。
这样Dagger注入时就可以在模块中寻找依赖对象的实例化方法
接着是Component
@Component(modules = arrayOf(MemberModule::class))
interface TargetComponent {
fun inject(target: Target)
}
Component多了一个modules参数,代表这个组件所包含的模块,一个组件可以包含多个模块,所以是参数是Module数组
这样就改造完成了,编译后,注入的代码和原来一样
DaggerTargetComponent.builder()
.build()
.inject(target)
看看生成的代码
可以看到,比之前多了一个MemberModule_ProvideMemberFactory
看回DaggerTargetComponent,这里比之前就多了一些代码
- builder多了一个参数,就是MemberModule
- 这个MemberModule可以不传,因为MemberModule是构造函数是无参的,能直接new
- 工厂不一样了,create的传参也不一样了
接下来看看这个工厂
对比一下可以发现,其实就是再把Member的依赖name外包出去给别的提供者提供实例,这种情况后面再讲
代码很简单,从原来直接new变成了MemberModule的provideMember()方法产生实例而已
4.1.1Module不直接提供Member实例
上文提到一个没用的工厂,那么什么时候会产生作用呢?
改造一下Module,这次Provide不直接提供Member实例,返回一个String即可(Member的构成函数就一个String类型的参数)
@Module
class MemberModule {
@Provides
fun provideMemberName(): String = "from module String provider"
// @Provides
// fun provideMember(): Member = Member("from module Module provider")
}
编译,看生成的代码
再来看看DaggerTargetComponent
还是一样的直接new,只不过参数name的实例化由MemberModule_ProvideMemberNameFactory提供
4.2参数对象可以直接创建
好,之前的Member的构造参数是一个String,这个Dagger可没法创建,那么如果是一个可创建的呢?例如Member又依赖一个MemberOfMember
class MemberOfMember @Inject constructor()
class Member @Inject constructor(val memberOfMember: MemberOfMember)
把Component变回最初的样子
@Component
interface TargetComponent {
fun inject(target: Target)
}
老样子,看生成的代码
再看看DaggerTargetComponent
看到这是不是有些明白了?这里总结一下依赖的实例化
- 看能不能够直接创建(带@Inject的空构造函数),能就直接new
- 不能直接创建的,看module中有没有提供直接实例化的方法
- 没有提供直接实例化方法的,看有没有能够满足依赖实例化所需所有参数的实例化方法(这里会是一个递归的过程,参数的实例化会重复123的步骤)
ps:所有可以被自动实例化(1,3的情况)的对象构造参数都必须带@Inject
ps:2 @Inject声明属性表示这个属性可以被注入,属性本身不能是final,也不能在包级别以下的权限
@Inject不是万能的,例如接口没法实例化,或者是第三方的代码