今天接觸了Dagger這套android的依賴注入框架(DI框架)。感受跟Spring 的IOC差點兒相同吧。這個框架它的優勢是它沒有採用反射技術(Spring是用反射的),而是用預編譯技術。由於基於反射的DI很是地耗用資源(空間,時間)java
由於現在開發都是用Android Studio了,因此我這裏大概講下配置Dagger框架的開發環境。需要怎麼作。android
(由於Android Studio中用Gradle。因此跟傳統咱們用Eclipse配置的話。直接導入jar包,有點不同。git
)github
在開始看個人博文前。但願你們有時間可以本身看下Dagger官網的文檔:http://square.github.io/dagger/app
相應的中文翻譯:http://fanxu.me/post/2013-07-18#main(主要就是這篇文章翻譯得很是具體,就是太長了,我主要對這文章進行了本身的理解還有本身的淺析哈,因此假設你時間充裕,並且足夠耐心。可以先看這個。)框架
Dagger是構建在Android annotations的基礎上的。假設你還不知道關於Android annotations,可以先看看我以前的一篇文章。ide
Android annotations淺析:http://blog.csdn.net/ljphhj/article/details/37601173函數
1、Android Studio 配置 Dagger 開源框架環境post
一、創建一個module字體
二、在module中會有build.gradle的文件
三、在文件裏的下列位置增長兩行紅色字體
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:19.+'
compile 'com.squareup.dagger:dagger:1.2.+'
provided 'com.squareup.dagger:dagger-compiler:1.2.+'
}
2、Dagger淺析
一、不論什麼新生事物的產生都有其特定的歷史背景,爲何會出現Dagger框架呢?
Dagger框架爲了簡化你的代碼,讓你把你的注意力轉移到真正需要關注的類上,比方:
咱們常說要經過工廠類來建立出產品類對象。
但是,每每咱們更重視的是產品類,而並不是工廠類,更不是它的生產過程。
二、Dagger框架怎麼用?
Dagger框架圖:
首先我要用大白話的形式讓你們明白幾個概念:
(如下類設計未必合理。僅僅爲了讓你們更好地理解)
1.@Inject注入對象
public class ClassRoom{
@Inject Student student1; //加上這個註解@Inject。 表示當前類ClassRoom要注入這樣一個類Student的對象
//但是居然我前面說Dagger不是經過反射機制。而是預編譯的技術,那麼它要怎麼知道Student類對象由誰提供出來呢?
}
2.由於注入對象沒有一個提供對象的地方是不可以的,因此引出了@Provides註解, 和 @Module的概念
//由於@Provides要包括在@Module凝視的類中,因此僅僅要函數中出現了@Provides就必需要在類上面加上@Module註解
@Module
public class StudentModule{
//加上了@Provides的方法,Dagger會去識別它的返回類型,當發現它的返回類型是Student類,上面第一步的@Inject就會來調用它,完畢注入。
@Provides Student provideStudent(){
return new Student();
}
}
約定@Provides函數以provide做爲前綴, @Module類以Module做爲後綴。
假設以上的你都理解了,那麼接下來的東東就好辦了!!
3.當咱們但願不管多少個地方注入Student這個類。咱們僅僅但願擁有一份「Student」的實例對象(單例),那麼咱們可以用到註解@Singleton 加在 @Provides註解的後面就能夠
@Provides @Singleton Student provideStudent(){
return new Student();
}
@Singleton 凝視對Dagger有效, 也僅僅在一個ObjectGraph中生效。 如果有多個ObjectGraph。 則有多個相應的@Singleton對象。
4.延遲注入 Lazy :(即:懶載入, 等到調用的時候才注入)
public class ClassRoom{
@Inject Lazy<Student> lazyStudent;
public void study(){
lazyStudent.get();//這樣就能獲得一個Student對象
}
}
5.提供者注入 Provider
有些狀況下, 你需要多個對象實例。 而不是僅僅注入一個對象實例。這時你可以利用Providerpublic class ClassRoom{
@Inject Provider<Student> providerStudent;
public void study(){
providerStudent.get(); //獲得對象1
providerStudent.get(); //獲得對象2
//對象1 和 對象2 是兩個不一樣的對象.
}
}
6.限定符註解 @Qualifier : 我的認爲有點像 「Web中本身定義標籤」的感受,也有點像 "C語言裏宏定義" 的樣子。
有些時候,單純類型(指這些主要的@Inject....等等)是不可以知足指定依賴的需求的。
在這種狀況下,咱們可以增長限定符凝視. 這種凝視自己有一個@Qualifier凝視。
如下是javax.inject中@Named的聲明代碼:
@Qualifier @Retention(RUNTIME) public @interface Named { String value() default ""; }
這樣寫完以後,咱們就新擁有了一個註解@Named, 來幫助咱們限定咱們想提供的類對象,還有咱們得到的類對象的實例。
如:
public class ClassRoom{
@Inject @Named("胖虎") Student pangHu;
@Inject @Named("李四") Student liSi;
//這樣就限定了所要獲取的Student類對象實例的性質了
}
那麼咱們以前說必需要有提供 類對象實例的方法,那麼現在有限定符的話,咱們要怎麼來寫這種方法呢?
@Module
public class StudentModule{
@Provides @Named("胖虎") Student providePangHuStudent(){
return new Student("胖虎"); //假設該Student類有個這種構造函數
}
@Provides @Named("李四") Student provideLiSiStudent(){
return new Student("李四");
}
}
依賴關係也可以同一時候有多重限定符凝視。
7.靜態注入(staticInjections):建議慎重使用這個特性。 由於靜態依賴注入很是難測試和複用。
Dagger可以注入靜態變量。擁有@Inject靜態變量的類必須在@Module的staticInjections中明白說明。
@Module( staticInjections = LegacyCoffeeUtils.class ) class LegacyModule { }
可以使用ObjectGraph.injectStatics()注入靜態變量:
ObjectGraph objectGraph = ObjectGraph.create(new LegacyModule()); objectGraph.injectStatics();
8.編譯時有效性的檢查(這個很是重要)
Dagger包括一個annotation 處理器, 這個處理器檢查module和注入的有效性。處理器很是嚴格, 如果有不論什麼綁定是無效或者不完整的, 將引起編譯錯誤。
那麼應該遵循什麼樣的規則呢?我舉官網上的一個樣例來跟你們分析下哈。
@Module
public class DripCoffeeModule {
@Provides
Heater provideHeater(Executor executor) {
return new CpuHeater(executor);
}
}
分析:由於咱們知道。provideHeater是在有別的類@Inject了Heater類(或子類)的時候。會來調用這種方法。也就是說這個全然是由Dagger框架來調用的,而並不是傳統意義上咱們本身調用這個函數。
那麼問題應該就很是明顯了吧???
這個參數。應該要由咱們來提供吧?確定有一個@Module裏面有一個方法是叫作 provideExecutor(), 但是咱們這邊也不能用@Inject來註解這個參數。那要怎麼讓咱們這個當前類。知道要去找那個方法拿這個參數呢?
有兩種方法:
1.把這個provideExecutor()方法放入到咱們這個類中來,這樣子它就可以找到這種方法並進行注入這個參數對象了。
2.在@Module中增長一個參數 complete=false, 標記說明該Module爲不完整的Module。
由於不完整的Module贊成缺乏對象實例
@Module(complete=false)
public class DripCoffeeModule {
@Provides
Heater provideHeater(Executor executor) {
return new CpuHeater(executor);
}
}
另外一個比較難理解的就是關於injects了
是這種,假設在@Module中增長參數injects (即所謂的:注入對象列表綁定)。
如果這個Module提供的對象綁定。 可能被injects列表中之外的類使用, 可以將改Module標記爲library, 以免出錯。
如:
@Module(
injects = ClassRoom.class,
library = true
)
public class StudentModule{
@Provides Student provideStudent(){
return new Student();
}
@Provides Others provideOthers(){
return new Others;
}
}
分析:由於ClassRoom中僅僅用到了一個Student的類,而injects列表中也僅僅寫了ClassRoom.class, 這種話。這個類提供的其它方法有可能被除了ClassRoom以外的類所用,那麼避免報錯就要在@Module加上參數library=true
9.所有@Provides要放在一個@Module中
由於Dagger規定所有@Provides要放在一個@Module中。因此咱們要麼可以在一個Module中用includes參數把其它的Module類包括進來
或者比較建議的是:再建立一個空的Module類,把所有的Module都包括到這個Module中來。
@Module( includes = { StudentModule.class, ExecutorModule.class } ) public class AppModules { }
10.Module重載(引用官網的樣例): 瞭解下就可以了
若對同一個依賴關係有多個@Provides函數, Dagger 將會報錯。
但在有些狀況下,是有必要替換production代碼的, 比方測試和開發。 在@Module中可以使用overrides =true 。 重載其綁定關係。
如下這個JUnit測試利用Mockito, 重載了DripCoffeeModule的Heater綁定關係。這個Mock對象將inject到CoffeeMake中。
public class CoffeeMakerTest { @Inject CoffeeMaker coffeeMaker; @Inject Heater heater; @Before public void setUp() { ObjectGraph.create(new TestModule()).inject(this); } @Module( includes = DripCoffeeModule.class, injects = CoffeeMakerTest.class, overrides = true ) static class TestModule { @Provides @Singleton Heater provideHeater() { return Mockito.mock(Heater.class); } } @Test public void testHeaterIsTurnedOnAndThenOff() { Mockito.when(heater.isHot()).thenReturn(true); coffeeMaker.brew(); Mockito.verify(heater, Mockito.times(1)).on(); Mockito.verify(heater, Mockito.times(1)).off(); } }
這種重載方式也很是適合程序的小型變更, 好比付費版。免費版。