Dagger2
的核心是 依賴注入,對於 依賴注入 的理解,每一個人都有一個本身的版本,個人理解是:經過注入的方式得到想要的依賴。java
在開始學習使用 Dagger2
以前,咱們最好先理解清楚這個框架幫咱們作了什麼,我在一開始使用 Dagger2
的時候也是這樣,一上來無論三七二十一先看怎麼用,結果致使的結果是用的過程當中一臉問號。緣由就是沒有理解這個框架它到底是幫咱們幹了什麼。git
Dagger2
是一個能幫助咱們經過註解的方式,快速完成依賴注入的優秀框架。程序員
不過也別過於神化 Dagger2
,要想使用好它,咱們仍是要遵循很多約定規則的,該寫的代碼也逃避不了。github
至於 Dagger2
好處,我以爲最大的好處就是解藕了,最直觀的一個解釋是:哪天代碼升級,須要修改一個在整個項目中大量被 new
的對象的構造方法時,若是用了 Dagger2
依賴注入,就能夠不用大範圍的修改代碼,只要修改依賴提供者 Module
裏面的 @Provides
註解的方法便可。固然上面說的都是有前提條件的,因此沒必要較真於此,畢竟咱們的目的是理解並使用 Dagger2
。bash
下面先逐個擊破 Dagger2
的註解。Let‘s go...app
@Inject
註解會在兩個地方被看到:框架
先看代碼:ide
// 車輪
public class Wheel {
@Inject
public Wheel(){
Log.e("Wheel", "我是自行車輪胎");
}
}
// 自行車
public class Bike {
// 方式一
@Inject
Wheel mWheel;
// 方式二
//@Inject
//public void setWheel(Wheel wheel){
// this.mWheel = wheel;
//}
public Bike() {
Log.e("Bike", "我是自行車");
DaggerBikeComponent.builder().build().inject(this);
}
}
// 自行車依賴注入器
@Component
public interface BikeComponent {
// 這個方法名能夠任意起,只是你們更習慣用 inject
// 由於inject 比較形象
void inject(Bike bike);
}
複製代碼
自行車對象依賴車輪,咱們使用 @Inject
註解了 Wheel
的構造方法,以及 Bike
中的 mWheel
屬性(或者被註釋的方法),結合被 @Component
註解的 BikeComponent
,咱們實現了最最簡單的依賴注入代碼。學習
DaggerBikeComponent
的起到橋樑的做用,鏈接 依賴需求者
和 依賴提供者
,後面會有更多講解。ui
DaggerBikeComponent
是Dagger2
自動幫咱們生成的,名稱的規則是Dagger{ Component 名稱 }
@Inject
不能使用 private
,打開 build 後生成的代碼咱們會找到緣由,這裏留給你們本身驗證;@Inject
修飾依賴構造方法的時候,若是存在多個構造方法,只能選擇修飾一個。至於要使用多個構造方法,後面會有解決方案「???????」。能夠看到,上面咱們爲了獲取 Wheel
依賴,是直接修改了 Wheel
的源碼,在其構造方法上動了些手腳,加上了 @Inject
註解。可是若是咱們使用的是一個第三方庫提供的類的話,@Inject
這招就行不通了。
@Module
登場!!!
先看代碼:
// 【新加】車燈,沒有被 @Inject 註解構造方法
// 三方提供
public class Light {
public Light(){
Log.e("Light", "我是車燈");
}
}
// 自行車
public class Bike {
@Inject
Wheel mWheel;
@Inject
Light mLight;
//@Inject
//public void setWheel(Wheel wheel){
// this.mWheel = wheel;
//}
public Bike() {
Log.e("Bike", "我是自行車");
// 【修改1】多了 .bikeModule(new BikeModule())
DaggerBikeComponent.builder().bikeModule(new BikeModule()).build().inject(this);
}
}
// 自行車依賴注入器 【修改2】指定了 modules 爲 BikeModule
@Component(modules = {BikeModule.class})
public interface BikeComponent {
void inject(Bike bike);
}
// 【新加】自行車依賴管理員
@Module
public class BikeModule {
// 名字沒有規定,
// 通常爲 provide+待被提供的依賴名稱
@Provides
Light provideLight(){
return new Light();
}
}
複製代碼
能夠將 @Module
註解的類理解爲依賴管理員,這個管理員經過 @Provides
註解的方法提供依賴給依賴需求者,好比上面的例子,經過 @Provides
註解了 Light provideLight()
方法,該方法實際直接返回了一個 Light
對象( 這裏就是我一開始講的,該寫的代碼仍是逃避不了,好比建立這個依賴的代碼仍是須要咱們手動的血出來 )。
Dagger2
在尋找依賴的時候有一個順序,會先從@Module
中尋找,若是找不到再從@Inject
註解了構造方法的類尋找,這個瞭解一下就好,在定位一些問題的時候會派上用場
@Component
註解的類能夠理解爲一個橋樑,這個橋樑的做用是鏈接 Module
(依賴管理員)和依賴需求者(好比依賴了 Wheel
的 Bike
),沒有這個中介,@Inject
,@Module
都會失去做用。
Component
會聲明一個 Module
集合,也就是說,一個 Componet
能夠有多個依賴管理員。
@Qualifier
註解的做用按字面理解便知一二,Qualifier
是 「限定」 的意思,意思能夠用它來限定一些東西,具體限定什麼,先看下面的例子,Light
類多個一個燈色的屬性:color
, 默認顏色是白色。
public class Light {
private String color = "white";
public Light() {
Log.e("Light", "我是車燈, 而且顏色是:" + this.color);
}
public Light(String color) {
this.color = color;
Log.e("Light", "我是車燈, 而且顏色是:" + this.color);
}
}
複製代碼
如今咱們的 Light
類有兩個構造方法,改寫一下依賴管理員:
@Module
public class BikeModule {
@Provides
Light provideLight() {
return new Light();
}
@Provides
Light provideRedLight() {
return new Light("red");
}
}
複製代碼
這個時候咱們 run build
一下,會獲得下面的編譯錯誤:
xxx.xx.xxx.Light is bound multiple times...
複製代碼
說明直接這樣寫確定不行,那麼能夠請 @Qualifier
登場了。首先咱們自定一個 Qualifier
:
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface LightColorQualifier {
String value() default "white";
}
複製代碼
下面調整一下 Module
和 @Inject
的地方就可讓代碼又從新跑起來了
@Module
public class BikeModule {
@LightColorQualifier()
@Provides
Light provideLight() {
return new Light();
}
@LightColorQualifier("red")
@Provides
Light provideRedLight() {
return new Light("red");
}
}
public class Bike {
@Inject
Wheel mWheel;
@LightColorQualifier("red")
@Inject
Light mLight;
//@Inject
//public void setWheel(Wheel wheel){
// this.mWheel = wheel;
//}
public Bike() {
Log.e("Bike", "我是自行車");
DaggerBikeComponent.builder().bikeModule(new BikeModule()).build().inject(this);
}
}
複製代碼
你能夠試着改變 @LightColorQualifier("red")
爲 @LightColorQualifier("blue")
, 會發現 build 又會失敗,這說明這個 @LightColorQualifier("red")
起到了一個限定的做用,依賴管理員會根據這個限定去拿須要的依賴給需求者。
講完 @Qualifier
的原理和做用,@Named
水到渠成,看源碼可知:
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
/** The name. */
String value() default "";
}
複製代碼
是的,@Named
只是一個官方幫咱們實現好的 @Qualifier
,沒啥特殊要求,直接用這個便可,至於在定義本身的 @Qualifier
的時候,下面幾個方面能夠你們本身嘗試探究一下:
@Named
用的 value()
,能否換成其餘的名字?String
以外,還能夠用其餘類型嗎,好比 int
,boolean
之類?這邊就不繞彎子了,@Scope
和 @Singleton
的關係,其實和上面的 @Qualifier
,@Named
同樣, @Singleton
是一個官方 @Scope
實現:
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
複製代碼
回到 Scope
,中文意思是 「範圍」,使用 @Scope
能夠約定一些聲明週期,好比咱們開發中常常會用到 @ActivityScope
,@FragmentScope
,可是是否是咱們只要使用了 @Singleton
,@ActivityScope
,@FragmentScope
。。依賴就會自動有了生命週期了呢?
固然不會,仍是開頭講的,不能神化 Dagger2
。。
@Singleton
並非真正意義上用來建立單列的。 其實 Dagger2
真正建立單列的流程是:
Module
實現全局類的實現AppComponent
中使用這個 Module
AppComponent
只會初始化一次,通常會在 Application
的 onCreate()
方法中建立這個 AppComponent
那麼 @Singleton
的做用是什麼:
Module
和 Component
之間的關係,保證 AppComponent
和 Module
是匹配的。若AppComponent
和 Module
的 Scope
是不同的,則會在編譯時報錯。Module
中建立的類實例是單例。最後整理一些約定:
@Provides
方法註解了 Scope
,@Component
必定要有註解@Component
註解了 Scope
,不必定非得有構造方法或者 @Provides
方法被 Scope
註解@Scope
的 @Component
能夠依賴有 @Scope
的 @Component
,有 @Scope
的 @Component
只能依賴有 @Scope
的 @Component
,而且二者的 @Scope
不能相同@Singleton
的 @Component
只能被依賴而不能依賴任何 @Component
好的程序員怎能不會偷懶???
Component
的複用主要有下面兩種方式:Component
中的 dependencies
屬性和 @Subcomponent
註解
@ActivityScope
@Component(modules = {MainModule.class } , dependencies = {AppComponent.class})
public interface MainComponent {
void inject(MainActivity mainActivity);
}
@Singleton
@Component(modules = { AppModule.class })
public interface AppComponent {
Gson gson();
}
複製代碼
這種方式,若是在 MainActivity
中想要獲取到 AppModule
裏面提供的依賴,必須在 AppComponent
裏面顯式的提供出來。否則的話是無法在 MainActivity
中獲取到 AppModule
中想要依賴的。
@ActivityScope
@Subcomponent(modules = {MainModule.class })
public interface MainComponent {
void inject(MainActivity mainActivity);
}
@Singleton
@Component(modules = { AppModule.class })
public interface AppComponent {
MainComponent plus(MainModule mainModule);
}
public class MainActivity extends AppCompatActivity {
@Inject Gson mGson;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DemoApp.getInstance().getAppComponent().plus(new MainModule(this)).inject(this);
//DaggerMainComponent.builder()
// .appComponent(DemoApp.getInstance().getAppComponent())
// .mainModule(new MainModule(this))
// .build()
// .inject(this);
Log.e("MainActivity", mGson.toString());
//new Bike();
}
}
複製代碼
@Subcomponent
註解的 Component
不會自動生成相似 DaggerxxxComponent
的類文件,實際上是把本身扔到了父 Component
中,從而得到到父 Component
的全部注入屬性, 好比上面咱們就沒有再顯式的在 AppComponent
中提供 Gson
對象了。
經過這兩個例子,你們能夠對比體會一下兩者的區別。我我的的理解是 dependencies
更加委婉,我打好招呼要啥再給啥,@Subcomponent
就有點粗暴了,直接本身跑到人家家裏各類拿,想拿啥拿啥。