破殼之Dagger2入門與使用

什麼是 Dagger2

Dagger2 的核心是 依賴注入,對於 依賴注入 的理解,每一個人都有一個本身的版本,個人理解是:經過注入的方式得到想要的依賴。java

在開始學習使用 Dagger2 以前,咱們最好先理解清楚這個框架幫咱們作了什麼,我在一開始使用 Dagger2 的時候也是這樣,一上來無論三七二十一先看怎麼用,結果致使的結果是用的過程當中一臉問號。緣由就是沒有理解這個框架它到底是幫咱們幹了什麼。git

Dagger2 是一個能幫助咱們經過註解的方式,快速完成依賴注入的優秀框架。程序員

不過也別過於神化 Dagger2,要想使用好它,咱們仍是要遵循很多約定規則的,該寫的代碼也逃避不了。github

至於 Dagger2 好處,我以爲最大的好處就是解藕了,最直觀的一個解釋是:哪天代碼升級,須要修改一個在整個項目中大量被 new 的對象的構造方法時,若是用了 Dagger2 依賴注入,就能夠不用大範圍的修改代碼,只要修改依賴提供者 Module 裏面的 @Provides 註解的方法便可。固然上面說的都是有前提條件的,因此沒必要較真於此,畢竟咱們的目的是理解並使用 Dagger2bash

下面先逐個擊破 Dagger2 的註解。Let‘s go...app

Dagger2 的註解

@Inject

@Inject 註解會在兩個地方被看到:框架

  1. 依賴的構造方法
  2. 依賴需求者的屬性或者方法

先看代碼: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

DaggerBikeComponentDagger2 自動幫咱們生成的,名稱的規則是 Dagger{ Component 名稱 }

@Inject 的一些約定:

  1. @Inject 不能使用 private,打開 build 後生成的代碼咱們會找到緣由,這裏留給你們本身驗證;
  2. @Inject 修飾依賴構造方法的時候,若是存在多個構造方法,只能選擇修飾一個。至於要使用多個構造方法,後面會有解決方案「???????」。

@Module & @Provides

能夠看到,上面咱們爲了獲取 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

@Component 註解的類能夠理解爲一個橋樑,這個橋樑的做用是鏈接 Module(依賴管理員)和依賴需求者(好比依賴了 WheelBike ),沒有這個中介,@Inject@Module 都會失去做用。

Component 會聲明一個 Module 集合,也就是說,一個 Componet 能夠有多個依賴管理員。

@Qualifier & @Named

@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 的時候,下面幾個方面能夠你們本身嘗試探究一下:

  1. 我上面是參考的官法 @Named 用的 value() ,能否換成其餘的名字?
  2. 能否定義多個變量?
  3. 變量除了 String 以外,還能夠用其餘類型嗎,好比 intboolean 之類?

@Scope & @Singleton

這邊就不繞彎子了,@Scope@Singleton 的關係,其實和上面的 @Qualifier@Named 同樣, @Singleton 是一個官方 @Scope 實現:

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
複製代碼

回到 Scope ,中文意思是 「範圍」,使用 @Scope 能夠約定一些聲明週期,好比咱們開發中常常會用到 @ActivityScope@FragmentScope,可是是否是咱們只要使用了 @Singleton@ActivityScope@FragmentScope。。依賴就會自動有了生命週期了呢?

固然不會,仍是開頭講的,不能神化 Dagger2。。

@Singleton 並非真正意義上用來建立單列的。 其實 Dagger2 真正建立單列的流程是:

  1. 首先建立一個 Module 實現全局類的實現
  2. 而後在 AppComponent 中使用這個 Module
  3. 保證 AppComponent 只會初始化一次,通常會在 ApplicationonCreate() 方法中建立這個 AppComponent

那麼 @Singleton 的做用是什麼:

  1. 更好的管理 ModuleComponent 之間的關係,保證 AppComponentModule 是匹配的。若AppComponentModuleScope 是不同的,則會在編譯時報錯。
  2. 代碼可讀性,讓別的人看代碼的時候更好的瞭解 Module 中建立的類實例是單例。

最後整理一些約定:

  1. 若是在構造方法或者 @Provides 方法註解了 Scope@Component 必定要有註解
  2. @Component 註解了 Scope,不必定非得有構造方法或者 @Provides 方法被 Scope 註解
  3. @Scope@Component 能夠依賴有 @Scope@Component,有 @Scope@Component 只能依賴有 @Scope@Component,而且二者的 @Scope 不能相同
  4. @Singleton@Component 只能被依賴而不能依賴任何 @Component

@Component 的複用

好的程序員怎能不會偷懶???

Component 的複用主要有下面兩種方式:Component 中的 dependencies 屬性和 @Subcomponent 註解

經過 dependencies 屬性指定依賴的 Component

@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 中想要依賴的。

@Subcomponent

@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 就有點粗暴了,直接本身跑到人家家裏各類拿,想拿啥拿啥。

最後奉上例子代碼

代碼連接

小武的知識鋪
相關文章
相關標籤/搜索