概述
在開發的過程中.我們都需要用到很多對象,在使用之前都需要初始化.如果這個對象需要在多處被使用,那麼在每個地方都要寫相同的代碼,而且當我們需要改變其中某個類的功能的時候,就需要更改大量的代碼
不僅麻煩,而且容易出錯.這時候就展現了依賴注入的好處,如果還不太明白什麼是依賴注入,請參考: 依賴注入,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
注解是很好用的實作依賴的途徑,然而它并不适用于所有情況
- 接口類型不能被構造(接口當然不能構造對象)
- 第三方的類不能被注釋構造(第三方的類顯然不能加入
注釋, 當然也不能被Dagger構造.)@Inject
- 可配置的對象必須被配置好(有些類需要靈活的初始化配置,而不是使用一個單一的構造函數)
對那些使用
@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中所有
方法的參數都必須在這個Module中提供相應的@Provides
@Provides
方法,
或者在
注解後添加@Module
注明這是一個不完整Module(即它會被其他Module所擴充)“complete = false”
- 一個Module中所有的
方法都要被它聲明的注入對象所使用,或者在@Module注解後添加@Provides
注明(即它是為了擴充其他Module而存在的)“library = ture”
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依賴注入架構淺析