如果還沒(méi)入門(mén)的症革,可以先去搜搜基本用法筐咧,本系列主要偏向原理
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平臺(tái)提供的一個(gè)完全靜態(tài)的噪矛,在編譯時(shí)進(jìn)行依賴(lài)注入的框架量蕊,原來(lái)是由Square公司維護(hù)的然后現(xiàn)在把這堆東西扔給Google維護(hù)了。Dagger解決了基于反射帶來(lái)的開(kāi)發(fā)和性能上的問(wèn)題(因?yàn)镈agger并沒(méi)有用反射來(lái)做依賴(lài)注入, 靠的是注解生成代碼實(shí)現(xiàn)依賴(lài)注入)
ps1:本文不講使用方法
ps2:本篇實(shí)例使用的都是kotlin
2.簡(jiǎn)單用法
舉個(gè)例子,Target類(lèi)里有一個(gè)Member類(lèi)的成員變量,也就是Target依賴(lài)Member
class Member constructor()
class Target(val member: Member)
看上去并沒(méi)有什么問(wèn)題,但是這并不是好的寫(xiě)法.
如果Target類(lèi)不單單依賴(lài)Member,還依賴(lài)很多類(lèi)的話(huà),就得寫(xiě)很多創(chuàng)建實(shí)例的代碼,甚至如果以后Member類(lèi)需要修改代碼時(shí),那還得修改Target類(lèi)的代碼.如果Member類(lèi)被很多類(lèi)依賴(lài)的話(huà),修改起來(lái)就更加困難.
所以dagger2就是用來(lái)完成這個(gè)實(shí)例化并賦值b = new Member();
的事情(也就是依賴(lài)注入)
dagger的注解有component\inject\module\scpoe
首先來(lái)最簡(jiǎn)單的寫(xiě)法艇挨,Member構(gòu)造方法沒(méi)有參數(shù)残炮,Target依賴(lài)Member的情況,也就是上面例子那樣的
給Member的構(gòu)造函數(shù)添加@Inject注解缩滨,告訴dagger此類(lèi)是可以實(shí)例化的
class Member @Inject constructor()
接著定義一個(gè)Component势就,也就是注入器(dagger就是根據(jù)這個(gè)接口生成實(shí)際的注入代碼)
@Component
interface TargetComponent {
fun inject(target:Target)
}
然后是Target泉瞻,member標(biāo)記@Inject表示該變量可以被注入(從上面生成注入器的操作也能看出注入的代碼不在類(lèi)本身里面,所以member變量是不能標(biāo)記為final或者私有的)
class Target {
@Inject
lateinit var member:Member
}
這樣標(biāo)注的過(guò)程就完成了苞冯,編譯之后袖牙,就會(huì)生成DaggerTargetComponent(名字為Dagger+你定義的Component接口)的名稱(chēng)的類(lèi),在需要注入的時(shí)候調(diào)用
val target = Target()
DaggerTestComponent.builder().build().inject(target)
這樣target里面的member實(shí)例就被注入進(jìn)去了
3.dagger生成的代碼解析
下面我們來(lái)看dagger生成的代碼舅锄,找到build/generated/kapt/debug/xxx(你的包名)
可以看到生成了三個(gè)類(lèi),
- DaggerTargetComponent:表示這層依賴(lài)關(guān)系的組件
- Member_Factory:提供依賴(lài)實(shí)例的工廠
- Target_MembersInjector:就是實(shí)際用于注入依賴(lài)的注入器
下面再來(lái)看這三個(gè)類(lèi)
3.1 DaggerTargetComponent
可以看到這是一個(gè)Builder模式(這里看上去簡(jiǎn)單皇忿,但依賴(lài)多了就復(fù)雜了)
這里主要看inject方法畴蹭,用注入器調(diào)用injectMembers方法,傳入的參數(shù)是我們需要注入Member的Target實(shí)例
跟進(jìn)injectMembers(target)方法
這就是一個(gè)dagger定義的接口鳍烁,那么就得看具體實(shí)現(xiàn)了叨襟,回到DaggerTargetComponent, 找到initialize方法,這里是注入器targetMembersInjector初始化的地方幔荒。
this.targetMembersInjector = Target_MembersInjector.create(Member_Factory.create());
先跟進(jìn)Member_Factory.create()方法芹啥,來(lái)到代碼生成的第二個(gè)類(lèi)Member_Factory
3.2 Member_Factory
這就是一個(gè)簡(jiǎn)單的工廠類(lèi),用來(lái)提供Member實(shí)例的铺峭,我們知道Member的構(gòu)造方法被標(biāo)記@Inject了墓怀,加上又沒(méi)有參數(shù),所以生成的代碼就直接new了
其他沒(méi)啥好講了卫键,回到前面的
this.targetMembersInjector = Target_MembersInjector.create(Member_Factory.create());
接下來(lái)就是跟進(jìn)Target_MembersInjector.create()方法了
3.3 Target_MembersInjector
這里主要就是injectMembers的實(shí)現(xiàn)了
instance.member = memberProvider.get();
memberProvider就是前面的工廠(Member_Factory實(shí)現(xiàn)了Factory<Member>傀履,而Factory<Member>繼承了Provider<Member>),提供了Member的實(shí)例
這里就能看出為啥Target的成員member不能是final(賦值在類(lèi)外)和至少得是包級(jí)的權(quán)限(同包不同類(lèi))
ps:關(guān)于生成代碼的包位置莉炉,基本上就是對(duì)應(yīng)的類(lèi)生成的包位置相同
- TargetComponent->DaggerTargetComponent
- Target->Target_MembersInjector
- Member->Member_Factory
4.依賴(lài)不能直接創(chuàng)建的情況
前面的例子中,Member的構(gòu)造函數(shù)是無(wú)參的钓账,那么如果有參數(shù)呢?
這里又分為兩種情況:
- 參數(shù)對(duì)象無(wú)法直接創(chuàng)建
- 參數(shù)對(duì)象可以直接創(chuàng)建
4.1參數(shù)對(duì)象無(wú)法直接創(chuàng)建
這里就是另外一個(gè)注解Module以及Provide的作用了絮宁,顧名思義梆暮,這Module就是Target所依賴(lài)的一個(gè)模塊提供者.
改寫(xiě)一下Member,構(gòu)造函數(shù)需要一個(gè)String類(lèi)型的參數(shù)
class Member @Inject constructor(val name: String)
然后創(chuàng)建一個(gè)MemberModule類(lèi)
@Module
class MemberModule {
@Provides
fun provideMember(): Member = Member("from module provider")
}
Module表示這是模塊绍昂,可以放在組件中啦粹。
Provides表示這是實(shí)例提供者。
這樣Dagger注入時(shí)就可以在模塊中尋找依賴(lài)對(duì)象的實(shí)例化方法
接著是Component
@Component(modules = arrayOf(MemberModule::class))
interface TargetComponent {
fun inject(target: Target)
}
Component多了一個(gè)modules參數(shù)窘游,代表這個(gè)組件所包含的模塊唠椭,一個(gè)組件可以包含多個(gè)模塊,所以是參數(shù)是Module數(shù)組
這樣就改造完成了忍饰,編譯后贪嫂,注入的代碼和原來(lái)一樣
DaggerTargetComponent.builder()
.build()
.inject(target)
看看生成的代碼
可以看到艾蓝,比之前多了一個(gè)MemberModule_ProvideMemberFactory
看回DaggerTargetComponent,這里比之前就多了一些代碼
- builder多了一個(gè)參數(shù)力崇,就是MemberModule
- 這個(gè)MemberModule可以不傳斗塘,因?yàn)镸emberModule是構(gòu)造函數(shù)是無(wú)參的,能直接new
- 工廠不一樣了亮靴,create的傳參也不一樣了
接下來(lái)看看這個(gè)工廠
對(duì)比一下可以發(fā)現(xiàn),其實(shí)就是再把Member的依賴(lài)name外包出去給別的提供者提供實(shí)例台猴,這種情況后面再講
代碼很簡(jiǎn)單朽合,從原來(lái)直接new變成了MemberModule的provideMember()方法產(chǎn)生實(shí)例而已
4.1.1Module不直接提供Member實(shí)例
上文提到一個(gè)沒(méi)用的工廠,那么什么時(shí)候會(huì)產(chǎn)生作用呢饱狂?
改造一下Module曹步,這次Provide不直接提供Member實(shí)例,返回一個(gè)String即可(Member的構(gòu)成函數(shù)就一個(gè)String類(lèi)型的參數(shù))
@Module
class MemberModule {
@Provides
fun provideMemberName(): String = "from module String provider"
// @Provides
// fun provideMember(): Member = Member("from module Module provider")
}
編譯休讳,看生成的代碼
再來(lái)看看DaggerTargetComponent
還是一樣的直接new,只不過(guò)參數(shù)name的實(shí)例化由MemberModule_ProvideMemberNameFactory提供
4.2參數(shù)對(duì)象可以直接創(chuàng)建
好俊柔,之前的Member的構(gòu)造參數(shù)是一個(gè)String筹麸,這個(gè)Dagger可沒(méi)法創(chuàng)建,那么如果是一個(gè)可創(chuàng)建的呢雏婶?例如Member又依賴(lài)一個(gè)MemberOfMember
class MemberOfMember @Inject constructor()
class Member @Inject constructor(val memberOfMember: MemberOfMember)
把Component變回最初的樣子
@Component
interface TargetComponent {
fun inject(target: Target)
}
老樣子物赶,看生成的代碼
再看看DaggerTargetComponent
看到這是不是有些明白了留晚?這里總結(jié)一下依賴(lài)的實(shí)例化
- 看能不能夠直接創(chuàng)建(帶@Inject的空構(gòu)造函數(shù))酵紫,能就直接new
- 不能直接創(chuàng)建的,看module中有沒(méi)有提供直接實(shí)例化的方法
- 沒(méi)有提供直接實(shí)例化方法的错维,看有沒(méi)有能夠滿(mǎn)足依賴(lài)實(shí)例化所需所有參數(shù)的實(shí)例化方法(這里會(huì)是一個(gè)遞歸的過(guò)程奖地,參數(shù)的實(shí)例化會(huì)重復(fù)123的步驟)
ps:所有可以被自動(dòng)實(shí)例化(1,3的情況)的對(duì)象構(gòu)造參數(shù)都必須帶@Inject
ps:2 @Inject聲明屬性表示這個(gè)屬性可以被注入赋焕,屬性本身不能是final参歹,也不能在包級(jí)別以下的權(quán)限
@Inject不是萬(wàn)能的,例如接口沒(méi)法實(shí)例化,或者是第三方的代碼