Dagger2使用簡析(一)——簡單注入

NOTE1 : Dagger2的官網地址: https://google.github.io/dagger/

NOTE2 : 使用前請先了解JAVA中的註解基本知識,如果對實現原理感興趣可以瞭解下APT技術(不瞭解也不影響正常使用)

NOTE3: 本文所涉及的示例已上傳至Github:
https://github.com/bailandi/Dagger2Demo


在具體使用前,首先來了解一個概念——依賴注入
依賴注入是由於類之間的依賴關係產生的,比如:

//產品依賴於操作工
public class Product{
  private Worker mWorker;
}

這個時候想要產生mWorker的實例,通常有兩種方法:

//直接new
mWorker = new Worker();

//通過參數傳入
public  Product(Worker worker){
  mWokrer = worker;
}

以上就是典型的依賴注入,那麼爲什麼要使用Dagger2來進行依賴注入呢?
在回答這個問題前,我們首先分析以上兩種產生mWorker實例的方式存在什麼問題。

  • 對於第一種方式,假設當業務擴充,此時Worker的構造需要依賴於工具小刀KnifeWorker的構造函數發生變化,我們不得不對Product做出修改,這嚴重違反了開閉原則
  • 對於第二種方式,假設工廠Factory依賴於Product,此時Product的位置就會與第一種的Worker一樣。我們可以從迪米特原則(最少知識原則)——一個類應該對自己直接耦合(依賴)的類知道最少中找到這一問題產生的根本原因,顯然,Factory並不關心Worker,然而Product卻在構造時將對Wokrer的依賴帶入到了Factory

爲了解決這種依賴關係對業務擴展時造成的糟糕體驗,我們使用Dagger2來進行依賴注入


一切實踐從問題出發,首先我們來解決第一個問題,

1. 如何使用Dagger2注入一個簡單對象到目標類中

讓我們先複製一個示例,它包含下面三部分

@Module
public class MainModule {
    @Provides
    public Product provideProduct() {
        return new Product();
    }
}

@Component(modules = {MainModule.class})
public interface MainComponent {
    void inject(MainActivity activity);
}

public class MainActivity extends AppCompatActivity {
    @Inject
    Product mProduct;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //...省略無關代碼

        DaggerMainComponent
                .builder()
                .build()
                .inject(this);

        mProduct.create();
    }
}

我們依次對三個部分所涉及的內容進行解釋:

1.1 @Module

@Module註解用於獲取對象實例的類,Dagger2根據該註解知道應該去哪個類裏獲取對象實例

1.2 @Provides

@Provides註解用於module類中獲取對象實例的方法,Dagger2根據該註解及方法的返回值類型將對象實例注入到對應的引用中

1.3 @Inject

@Inject用於註解構造函數或成員變量

  • 當作用於成員變量時,Dagger2根據該註解及成員變量的類型,從moudle中得到相應實例。
    注意:成員變量的訪問修飾符不能是private

  • 當作用於構造函數時,就是Dagger2對於獲取對象實例的第二種方式,比如上面的例子其實可以直接在Product的構造函數上註解@Inject,並移除@Provides註解的方法

     @Inject
     public Product() {
     }
    

    但是@Inject並非適用於所有的地方,比如以下三種情況:

    • 不具有構造函數的接口
    • 遠程引入的三方庫中的類無法被添加註解
    • 通過建造者模式等方式可配置化的進行構造的對象

    而使用@Provides可以更好的處理這些問題

1.4 @Component

@Component註解用於擔任連接橋樑的接口,其兩端分別是在@Component的參數中指定的modules數組在方法參數中指定的具體類
注意:方法參數必須是要注入的具體類,而非其父類或接口

1.5 DaggerMainComponent

在創建Module和Component後,我們需要build ——> Make Project,這樣APT會在指定目錄下生成對應的java文件,其中就會包含名如DaggerXX的的文件,它實現了我們所創建的Component接口,最終要完成注入,只需要像這樣調用:

  DaggerMainComponent
                .builder()
                .build()
                .inject(this);

2. 簡單分析自動生成的代碼

2.1 DaggerXX類

public final class DaggerMainComponent implements MainComponent {
  private final MainModule mainModule;

  private DaggerMainComponent(MainModule mainModuleParam) {
    this.mainModule = mainModuleParam;
  }

  public static Builder builder() {
    return new Builder();
  }

  public static MainComponent create() {
    return new Builder().build();
  }

  @Override
  public void inject(MainActivity activity) {
    injectMainActivity(activity);
  }

  private MainActivity injectMainActivity(MainActivity instance) {
    MainActivity_MembersInjector.injectMProduct(
        instance, MainModule_ProvideProductFactory.provideProduct(mainModule));
    return instance;
  }

  public static final class Builder {
    private MainModule mainModule;

    private Builder() {}

    public Builder mainModule(MainModule mainModule) {
      this.mainModule = Preconditions.checkNotNull(mainModule);
      return this;
    }

    public mainComponent build() {
      if (mainModule == null) {
        this.mainModule = new mainModule();
      }
      return new DaggerMainComponent(mainModule);
    }
  }
}

它實現了我們所創建的Component接口,內部使用了建造者模式,方便根據不同需求構建該對象實例,比如我們可以傳入module實例替換默認的無參實例,關鍵看一下我們定義的inject方法的具體實現。

2.2 MainActivity_MembersInjector類和MainModule_ProvideProductFactory類

public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
  //...省略無關代碼

  public static void injectMProduct(MainActivity instance, Product mProduct) {
    instance.mProduct = mProduct;
  }
}
public final class MainModule_ProvideProductFactory implements Factory<Product> {
   //...省略無關代碼

  public static Product provideProduct(MainModule instance) {
    return Preconditions.checkNotNull(
        instance.provideProduct(), "Cannot return null from a non-@Nullable @Provides method");
  }
}

其實關鍵的方法就是injectMProduct()provideProduct(),一看就懂,沒什麼可細說的。

3. 含有參數(依賴關係)的構造

Product必須與Worker關聯時,有如下構造函數

 public Product(Worker worker) {
     mWorker = worker;
 }

此時我們提供Product實例對象時需要作出以下修改

@Provides
public Product provideProduct(Worker worker) {
    return new Product(worker);
}

即我們需要傳入Worker來構建Product,那麼Worker實例如何提供呢?其實同樣的,我們可以使用簡單構造時的兩種方式提供Worker實例,具體就不在贅述,可以自行實踐。

下一篇
,我們將對問題進行升級,並瞭解@Scope、@Qualifier、@binds、dependencies、Lazy的使用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章