天天看點

Android 依賴注入架構 Dagger 學習

概述

在開發的過程中.我們都需要用到很多對象,在使用之前都需要初始化.如果這個對象需要在多處被使用,那麼在每個地方都要寫相同的代碼,而且當我們需要改變其中某個類的功能的時候,就需要更改大量的代碼

不僅麻煩,而且容易出錯.這時候就展現了依賴注入的好處,如果還不太明白什麼是依賴注入,請參考: 依賴注入,Dagger就是一款依賴注入架構

Dagger

dagger的用途就是 你不用初始化對象,達到成員變量申明就能用.dagger通過依賴注入建構對象圖表,降低了程式的耦合性.

通過

inject()

方法自動注入所有對象,完成自動的初始化。

Dagger使用

@Inject

注釋的構造函數 建立類對象。 當請求建構新的類對象時, Dagger 将自動擷取相應的參數, 并調用構造函數。

class  A{
        @Inject
        public A() {        
        }
    }
           
public class MainActivity extends Activity {  
    @Inject A a;  
}  
           

以上面的代碼為例,在Activity中執行個體化A的對象,隻需要

@Inject

即可,dagger會自動找到被标記的構造函數并調用來擷取執行個體.

如果構造函數中包含有參數.需要這個參數的構造函數也有

@Inject

标記.或者可以通過

@Provides

标記來擷取執行個體

官方示例

dagger 官方示例采用了煮咖啡來講解dagger的工作原理.

class Thermosiphon implements Pump {
  private final Heater heater;

  @Inject
  Thermosiphon(Heater heater) {
    this.heater = heater;
  }

  @Override public void pump() {
    if (heater.isHot()) {
      System.out.println("=> => pumping => =>");
    }
  }
}
           

這裡提供了一個

Pump

實作類的注入入口.同時又依賴于

Heater

class CoffeeMaker {
  @Inject Lazy<Heater> heater; // Don't want to create a possibly costly heater until we need it.
  @Inject Pump pump;

  public void brew() {
    heater.get().on();
    pump.pump();
    System.out.println(" [_]P coffee! [_]P ");
    heater.get().off();
  }
}
           

擷取

Heater

對象, 并注入到成員變量heater,同時擷取

Pump

對象并注入到成員變量pump。

pump 的依賴上面已經标注,那麼我們來看一下heater的實作

class ElectricHeater implements Heater {
  boolean heating;

  @Override public void on() {
    System.out.println("~ ~ ~ heating ~ ~ ~");
    this.heating = true;
  }

  @Override public void off() {
    this.heating = false;
  }

  @Override public boolean isHot() {
    return heating;
  }
}
           

可以看到 heater中是沒有

@Inject

标記的.

注意:類中含有

@Inject

注釋的成員變量, 卻沒有

@Inject

注釋的構造函數時, Dagger将使用類的預設構造函數。若類中缺少

@Inject

注釋, 該類是不能由Dagger建立的。

Dagger 通過構造相應類型的對象來實作依賴關系,那麼heater是怎麼引入的呢,上面我們說過還有一種方式那就是通過

@Provides

标記

自定義依賴(Provides)

構造方法進行

@Inject

注解是很好用的實作依賴的途徑,然而它并不适用于所有情況

  • 接口類型不能被構造(接口當然不能構造對象)
  • 第三方的類不能被注釋構造(第三方的類顯然不能加入

    @Inject

    注釋, 當然也不能被Dagger構造.)
  • 可配置的對象必須被配置好(有些類需要靈活的初始化配置,而不是使用一個單一的構造函數)

對那些使用

@Inject

效率極低或者awkward的情況,dagger使用了

@Provides

來實作依賴關系,我們來看一下heater的

provide

方法

@Provides @Singleton Heater provideHeater() {
    return new ElectricHeater();
  }
           

Dagger支援單例,實作方式也十分簡單,通過注解

@Singleton

,對象隻會被初始化一次,之後的每次都會被直接注入相同的對象。

同樣,

@Provides

注解的方法如果含有參數,它的所有參數也要保證能夠被Dagger擷取到。

@Provides Pump providePump(Thermosiphon pump) {
    return pump;
  }
           

Module

所有的

@Provides

函數必須屬于一個Module。這些Module類使用

@Module

注釋。

通常情況下, 約定

@Provides

函數以provide作為字首,

@Module

類以Module作為字尾。

@Module(
    injects = CoffeeApp.class,
    includes = PumpModule.class
)
class DripCoffeeModule {
  @Provides @Singleton Heater provideHeater() {
    return new ElectricHeater();
  }
}
           

注意:

  • 所有含有依賴注入的類,都必須顯示申明在Module 中
  • 一個Module中所有

    @Provides

    方法的參數都必須在這個Module中提供相應的

    @Provides

    方法,

    或者在

    @Module

    注解後添加

    “complete = false”

    注明這是一個不完整Module(即它會被其他Module所擴充)
  • 一個Module中所有的

    @Provides

    方法都要被它聲明的注入對象所使用,或者在@Module注解後添加

    “library = ture”

    注明(即它是為了擴充其他Module而存在的)

ps:

@Module(complete = false, library = true)
class PumpModule {
  @Provides Pump providePump(Thermosiphon pump) {
    return pump;
  }
}
           

這個

Module

就是為了擴充

DripCoffeeModule

而存在的,上面的

injects

選項使得可以在編譯的過程中檢查對象圖表是有有效

ObjectGraph(對象圖表)

@Inject

@Provides

注釋的類建構了一個對象圖表。這些對象與對象之間通過依賴關系互相關聯。

通過函數

ObjectGraph.create()

擷取這個對象圖表, 這個函數可以接受一個或多個Module作為參數

public class CoffeeApp implements Runnable {
  @Inject CoffeeMaker coffeeMaker;

  @Override public void run() {
    coffeeMaker.brew();
  }

  public static void main(String[] args) {
    ObjectGraph objectGraph = ObjectGraph.create(new DripCoffeeModule());
    CoffeeApp coffeeApp = objectGraph.get(CoffeeApp.class);
    coffeeApp.run();
  }
}
           

這裡面直接通過

objectGraph.get(CoffeeApp.class)

來擷取CoffeeApp 的執行個體,而我們的對象圖表中并沒有被引導注入

需要在Module中顯示的申明,上面

injects = CoffeeApp.class

, 就是這個作用

LAZY INJECTIONS(懶加載)

Sometimes you need an object to be instantiated lazily. For any binding T, you can create a Lazy which defers instantiation until the first call to Lazy’s get() method. If T is a singleton, then Lazy will be the same instance for all injections within the ObjectGraph.

懶加載, 等到調用的時候才注入

class GridingCoffeeMaker {
  @Inject Lazy<Grinder> lazyGrinder;

  public void brew() {
    while (needsGrinding()) {
      // Grinder created once on first call to .get() and cached.
      lazyGrinder.get().grind();
    }
  }
}
           

PROVIDER INJECTIONS(提供者注入 )

Sometimes you need multiple instances to be returned instead of just injecting a single value. While you have several options (Factories, Builders, etc.) one option is to inject a Provider instead of just T. A Provider creates a new instance of T each time .get() is called.

有些情況下, 你需要多個對象執行個體, 而不是僅僅注入一個對象執行個體。這時你可以利用Provider實作, 每次調用Provider的get()函數将傳回新的的對象執行個體。

class BigCoffeeMaker {
  @Inject Provider<Filter> filterProvider;

  public void brew(int numberOfPots) {
    ...
    for (int p = ; p < numberOfPots; p++) {
      maker.addFilter(filterProvider.get()); //new filter every time.
      maker.addCoffee(...);
      maker.percolate();
      ...
    }
  }
}
           

QUALIFIERS(限定符)

區分不能通過類型來擷取具體依賴的情況

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
  String value() default "";
}
           

我們的Module

@Provides @Named("hot plate") Heater provideHotPlateHeater() {
  return new ElectricHeater();
}

@Provides @Named("water") Heater provideWaterHeater() {
  return new ElectricHeater();
}
           

@Inject 引用,就是通過新增一個新注解,來擷取我們想要的執行個體

class ExpensiveCoffeeMaker {
  @Inject @Named("water") Heater waterHeater;
  @Inject @Named("hot plate") Heater hotPlateHeater;
  ...
}
           

參考:

Dagger——Android上的依賴注入架構

Dagger - 快速依賴注入器(for android and java) (1)

Dagger官方文檔

Android Dagger依賴注入架構淺析