概覽:
什么是Dagger2例获?
既然有Dagger2那么必然有Dagger1的存在妇多,Dagger1是大名鼎鼎的Square公司受到Guice啟發(fā)而開發(fā)的依賴注入框架啼辣,而Dagger2是Dagger1的分支,由谷歌公司接手開發(fā)畏腕。為什么使用Dagger2?
1.有利于模塊間的解耦,組件依賴的注入獨(dú)立于業(yè)務(wù)模塊之外
2.能夠清晰地管理實(shí)例的有效范圍
3.因?yàn)閷ο蟮膶?shí)例化獨(dú)立于業(yè)務(wù)茉稠,所以當(dāng)對象的實(shí)例化方法改變時(shí)描馅,不需要大量地修改業(yè)務(wù)代碼
4.在編譯期即完成依賴的注入,完全靜態(tài)(說白了就是拋棄了反射)
深入研究
前言講了那么多而线,接下來著重從原理和代碼方面進(jìn)行分析
引入Dagger2
Dagger2的引入也非常方便铭污,首先在項(xiàng)目的gradle中配置:
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
}```
然后在app的gradle中配置:
apply plugin: 'com.neenbedankt.android-apt'
...
dependencies {
apt 'com.google.dagger:dagger-compiler:2.0'
compile 'com.google.dagger:dagger:2.0'
}
可以看到gradle中出現(xiàn)了apt相關(guān)的配置恋日,可以猜到Dagger2的生成與apt技術(shù)相關(guān),即通過定義編譯期的注解嘹狞,再通過繼承Proccesor生成代碼邏輯岂膳,有興趣的同學(xué)可以深入學(xué)習(xí)一番
### 使用詳解
引入了Dagger2之后,首先解釋一下Dagger2中的四種常用用法
- _ @Inject_
主要有兩種用法:一是標(biāo)注相關(guān)的構(gòu)造方法磅网,那么Dagger2在使用時(shí)會(huì)找到標(biāo)注有@Inject的構(gòu)造方法來實(shí)例化谈截;第二種是標(biāo)注在相關(guān)的變量,告知Dagger2為其提供依賴
- _@Component_
@Component用于標(biāo)注接口涧偷,是依賴需求方和依賴提供方之間的橋梁簸喂。被Component標(biāo)注的接口在編譯時(shí)會(huì)生成該接口的實(shí)現(xiàn)類(如果@Component標(biāo)注的接口為ApplicationComponent,則編譯期生成的實(shí)現(xiàn)類為DaggerApplicationComponent),我們通過調(diào)用這個(gè)實(shí)現(xiàn)類的方法完成依賴注入燎潮;
- _@Module_
@Module用于標(biāo)注提供依賴的類喻鳄,但涉及到一些第三方包中的構(gòu)造方法,或者有參數(shù)的構(gòu)造方法時(shí)跟啤,我們無法使用@Inject诽表,轉(zhuǎn)而使用@Module進(jìn)行標(biāo)注
- _@Provide_
@Provide
用于標(biāo)注@Module中的方法,提供實(shí)例化的操作隅肥,并在需要時(shí)將依賴注入標(biāo)注了@Inject的變量
隨后這幾種注解結(jié)合上述基本用法竿奏,有意想不到的妙用
- _@Qulifier_
@Qulifier 為了區(qū)分不同使用不同類型生成的實(shí)例,比如@ForActivity 或@ForApplication
- _@Scope_
可限定變量的生效范圍腥放,形成局部單例
- _@Singleton_
這個(gè)就比較好理解了泛啸,Application級別的單例
只是介紹了基本用法,可能理解起來還是一頭霧水秃症,我們接下來結(jié)合代碼進(jìn)行進(jìn)一步闡述
1.僅使用@Inject
首先定義實(shí)體類Coke,并用@Inject標(biāo)注構(gòu)造方法
public class Coke {
@Inject
public Coke() {
Log.d("process", "a cup of coke was made");
}
}
第二步創(chuàng)建Component候址,命名為CokeComponent。inject方法即代表依賴注入至MainActivity
@Component
public interface CokeComponent
{
void inject(MainActivity activity);
}
在MainActivity中定義變量种柑,并用@Inject標(biāo)注
```java
public class MainActivity extends AppCompatActivity
{
//定義變量并標(biāo)注
@Inject Coke coke;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//DaggerCokeComponent由CokeComponent生成岗仑,調(diào)用下面的方法實(shí)現(xiàn)依賴注入
DaggerCokeComponent.builder().build().inject(this);
}
}
運(yùn)行程序,則log中打印出:
D/process: a cup of coke was made
然而事實(shí)上聚请,我們更多遇到是構(gòu)造函數(shù)含參或者無法直接打上@Inject標(biāo)記的情況荠雕,那么此時(shí)就要使用@Module進(jìn)行依賴注入
2.結(jié)合@Module使用
假設(shè)我們現(xiàn)在制造一瓶可樂的構(gòu)造方法為:public Coke(String ingredient)
,然后創(chuàng)建一個(gè)Module驶赏,并命名為CokeModule炸卑。用@Provide標(biāo)注提供實(shí)例的方法,并在方法體內(nèi)加入實(shí)例化相關(guān)的代碼
@Module
public class CokeModule
{
public CokeModule() {}
@Provides
Coke provideCoke() {
return new Coke("with suger");
}
}
并對Component進(jìn)行一點(diǎn)修改,告知Dagger2使用CokeModule提供依賴
@Component (modules = {CokeModule.class})
public interface CokeComponent {
void inject(MainActivity activity);
}
運(yùn)行程序煤傍,結(jié)果為:
D/process: a cup of coke was made with suger
3.結(jié)合@Qulifier使用
首先用@Qulifier定義兩個(gè)注解
@Qualifier
@Retention (RetentionPolicy.RUNTIME)
public @interface QulifierNonSuger {}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface QulifierSuger {}
其次對CokeModule進(jìn)行一點(diǎn)改造盖文,支持兩種實(shí)例化方式
@Module
public class CokeModule {
public CokeModule() {}
@QulifierSuger
@Provides
Coke provideCokeWithSuger() {
return new Coke("with suger");
}
@QulifierNonSuger
@Provides
Coke provideCokeWithoutSuger() {
return new Coke("without suger");
}
}
在MainActivity創(chuàng)建兩個(gè)Coke變量,并分別用兩種Qulifier注解
@QulifierSuger
@Inject
Coke cokeA;
@QulifierNonSuger
@Inject
Coke cokeB;
運(yùn)行程序蚯姆,兩杯不同的可樂就制造出來啦
D/process: a cup of coke was made with suger
D/process: a cup of coke was made without suger
通過上圖五续,我們可以清晰地看出洒敏,Module中通過兩種Qulifier注解表示了兩種實(shí)例化對象的方法,Component通過Module中提供的方法將依賴注入標(biāo)注了不同Qulifier的兩個(gè)變量
4.結(jié)合@Scope使用
好吧返帕,我們繼續(xù)進(jìn)行改造~
第一步桐玻,定義注解ActivityScope
@Scope
@Retention (RetentionPolicy.RUNTIME)
public @interface ActivityScope {}
然后用該Scope標(biāo)注Component和Module
@Module
public class CokeModule {
public CokeModule() {}
@ActivityScope
@Provides
Coke provideCokeWithSuger() {
return new Coke("with suger");
}
}
@ActivityScope
@Component(modules = {CokeModule.class})
public interface CokeComponent {
void inject(MainActivity activity);
}
我們在MainActivity中定義了兩個(gè)Coke變量,但可以看到Log只打印了一次荆萤,即代表Coke變量在MainActivity范圍內(nèi)是局部單例的
5.擴(kuò)展應(yīng)用
如圖镊靴,Component之間也可以有依賴關(guān)系,Dagger2的實(shí)際運(yùn)用中链韭,我們常定義一個(gè)應(yīng)用級別單例的ApplicationComponent來保持對Application的依賴偏竟,根據(jù)不同粒度(Activity級別或者頁面級別)創(chuàng)建依賴于ApplicationComponent的其余Component進(jìn)行整個(gè)應(yīng)用的依賴注入控制
至此,一個(gè)簡單的依賴注入使用案例已經(jīng)結(jié)束敞峭,但大多數(shù)的同學(xué)應(yīng)該還意猶未盡吧踊谋,因此我們接下來從Dagger2自動(dòng)生成的代碼層面深入了解Dagger2是如何工作的。
原理探究
這里我們以例子中的情況2作樣本旋讹,Dagger2生成的代碼可以在Build->generated->source->apt中找到
- CokeModule_ProvideCokeWithSugerFactory
@Generated("dagger.internal.codegen.ComponentProcessor")
public final class CokeModule_ProvideCokeWithSugerFactory implements Factory<Coke> {
private final CokeModule module;
public CokeModule_ProvideCokeWithSugerFactory(CokeModule module) {
assert module != null;
this.module = module;
}
@Override public Coke get() { Coke provided = module.provideCokeWithSuger();
if (provided == null) {
throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");
}
return provided; }
public static Factory<Coke> create(CokeModule module) {
return new CokeModule_ProvideCokeWithSugerFactory(module);
}
}
代碼比較簡單殖蚕,構(gòu)造方法中傳入了CokeModule,get()中通過我們在Module創(chuàng)建的provideCokeWithSugaer()拿到Coke的實(shí)例沉迹,而create方法我們稍后進(jìn)行解釋
- MainActivity_MembersInjector
@Generated("dagger.internal.codegen.ComponentProcessor")
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final MembersInjector<AppCompatActivity> supertypeInjector;
private final Provider<Coke> cokeAndCokeAProvider;
public MainActivity_MembersInjector(MembersInjector<AppCompatActivity> supertypeInjector, Provider<Coke> cokeAndCokeAProvider) {
assert supertypeInjector != null;
this.supertypeInjector = supertypeInjector;
assert cokeAndCokeAProvider != null;
this.cokeAndCokeAProvider = cokeAndCokeAProvider;
}
@Override public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference"); }
//依賴注入
supertypeInjector.injectMembers(instance);
instance.coke = cokeAndCokeAProvider.get();
}
public static MembersInjector<MainActivity> create(MembersInjector<AppCompatActivity> supertypeInjector, Provider<Coke> cokeAndCokeAProvider) {
return new MainActivity_MembersInjector(supertypeInjector, cokeAndCokeAProvider);
}
}
構(gòu)造方法中傳入了Injector和Provider睦疫,injectMembers()中將instance(MainActivity)通過回調(diào)傳給Injector,并從Provider中取出Coke的實(shí)例賦值給instance鞭呕。代碼最后又出現(xiàn)了create蛤育,我們可以肯定有一個(gè)橋梁將兩者串聯(lián)在一起,那就是——請繼續(xù)往下看
- DaggerCokeComponent
@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerCokeComponent implements CokeComponent {
private Provider<Coke> provideCokeWithSugerProvider;
private MembersInjector<MainActivity> mainActivityMembersInjector;
private DaggerCokeComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
public static CokeComponent create() {
return builder().build();
}
private void initialize(final Builder builder) {
this.provideCokeWithSugerProvider = ScopedProvider.create(CokeModule_ProvideCokeWithSugerFactory.create(builder.cokeModule));
this.mainActivityMembersInjector = MainActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), provideCokeWithSugerProvider); }
@Override public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}
public static final class Builder {
private CokeModule cokeModule;
private Builder() { }
public CokeComponent build() {
if (cokeModule == null) {
this.cokeModule = new CokeModule();
}
return new DaggerCokeComponent(this);
}
public Builder cokeModule(CokeModule cokeModule) {
if (cokeModule == null) {
throw new NullPointerException("cokeModule");
}
this.cokeModule = cokeModule;
return this;
}
}
}
謎底揭開了葫松,結(jié)合我們在MainActivity中注入依賴的方法:
DaggerCokeComponent.builder().cokeModule(new CokeModule()).build().inject(this)
在Builder中瓦糕,傳入創(chuàng)建的CokeModule,可以看到Dagger2貼心地幫我們做了缺省設(shè)置腋么,直接調(diào)用build()也可以生成CokeModule咕娄。然后調(diào)用DaggerCokeComponent的構(gòu)造方法,在initialize中我們終于看到了剛才的兩個(gè)create方法珊擂,也就是通過這座橋梁完成了實(shí)例的提供和注入谭胚。
后記
1.文中的圖摘自CodePath Android Cliffnotes,原文為:Dependency Injection with Dagger 2
2.注意事項(xiàng):當(dāng)更新Dagger2的版本時(shí)未玻,需重新clean項(xiàng)目,否則會(huì)出現(xiàn)actual and former argument lists different in length