什麼是Dagger2 Dagger是為Android和Java平臺提供的一個完全靜態的,在編譯時進行依賴註入的框架,原來是由Square公司維護,現在由Google維護。 我們知道Dagger是一個依賴註入的框架,那麼什麼是依賴註入呢? 我們在activity中有可能會用到很多很多的類,這些類要在a ...
什麼是Dagger2
Dagger是為Android和Java平臺提供的一個完全靜態的,在編譯時進行依賴註入的框架,原來是由Square公司維護,現在由Google維護。
我們知道Dagger是一個依賴註入的框架,那麼什麼是依賴註入呢?
我們在activity中有可能會用到很多很多的類,這些類要在activity中進行實例化,這樣就導致我們的activity非常依賴這麼多的類,這樣的程式耦合非常
嚴重,不便於維護和擴展,有什麼辦法可以不去依賴這些類呢,這時候就需要有一個容器(IoC),將這些類放到這個容器里並實例化,我們activity在用
到的時候去容器裡面取就可以了,我們從依賴類到依賴這個容器,實現瞭解耦,這就是我所理解的依賴註入,即所謂控制反轉;
簡單的說 Dagger就是用來創造這個容器,所有需要被依賴的對象在Dagger的容器中實例化,並通過Dagger註入到合適的地方,實現解耦,MVP框架就是為解耦而生,因此MVP和Dagger是絕配;
舉個慄子?
通常情況下我們引用一個類的做法:
我們先定義一個簡單的類:
1 public class User { 2 private String name; 3 4 public String getName() { 5 return name; 6 } 7 8 public void setName(String name) { 9 this.name = name; 10 } 11 }
在Activity中對其操作
1 private void initData() { 2 3 User user = new User(); 4 5 user.setName("測試"); 6 }
以上是最普通的用法
接下來我們來看Dagger2的用法
我們先來配置一下Dagger2
首先在項目的 build.gradle:
1 dependencies { 2 classpath 'com.android.tools.build:gradle:1.5.0' 3 classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' 4 classpath 'me.tatarka:gradle-retrolambda:3.2.0' 5 // NOTE: Do not place your application dependencies here; they belong 6 // in the individual module build.gradle files 7 }
然後是APP的 build.gradle
1 apply plugin: 'com.android.application' 2 apply plugin: 'com.neenbedankt.android-apt' 3 apply plugin: 'me.tatarka.retrolambda' 4 android { 5 compileSdkVersion 23 6 buildToolsVersion "23.0.1" 7 8 defaultConfig { 9 applicationId "jiao.com.jiaoproject" 10 minSdkVersion 15 11 targetSdkVersion 23 12 versionCode 1 13 versionName "1.0" 14 } 15 buildTypes { 16 release { 17 minifyEnabled false 18 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 } 20 } 21 22 compileOptions { 23 sourceCompatibility JavaVersion.VERSION_1_8 24 } 25 26 } 27 28 dependencies { 29 compile fileTree(dir: 'libs', include: ['*.jar']) 30 testCompile 'junit:junit:4.12' 31 compile 'com.android.support:appcompat-v7:23.3.0' 32 compile 'com.android.support:design:23.3.0' 33 apt 'com.google.dagger:dagger-compiler:2.2' 34 provided 'org.glassfish:javax.annotation:10.0-b28' 35 compile 'com.google.dagger:dagger:2.2' 36 compile 'com.jakewharton:butterknife:7.0.1' 37 compile 'com.squareup.okhttp3:logging-interceptor:3.3.0' 38 }
首先
1 public class User { 2 private String name; 3 4 @Inject 5 public User() { 6 } 7 8 public String getName() { 9 return name; 10 } 11 12 public void setName(String name) { 13 this.name = name; 14 } 15 }
發現有什麼變化了沒?@Inject是什麼東東?待會我們來說;
接著我們看怎麼使用
1 @Inject 2 User user; 3 4 private void initData() { 5 6 user.setName("測試"); 7 }
這時候我們允許程式發現空指針了;因為還缺少一個東西;
1 @Component 2 public interface ActivityComponent { 3 4 void inject(MainActivity MainActivity); 5 }
加上這個類之後 並且在Activity中對其初始化 完整代碼如下:
1 @Inject 2 User user; 3 4 private void initData() { 5 DaggerActivityComponent.builder().build().inject(this); 6 user.setName("測試"); 7 }
這時候發現我們的user對象可以正常使用了;看上去感覺挺複雜的,但是對於大型項目引用的類過多的時候,Dagger的優勢就體現出來了;
接下來我一一解答你們的疑惑;
首先我們來瞭解這幾個基礎概念:
- @Inject Inject主要有兩個作用,一個是使用在構造函數上,通過標記構造函數讓Dagger2來使用(Dagger2通過Inject標記可以在需要這個類實 例的時候來找到這個構造函數並把相關實例new出來)從而提供依賴,另一個作用就是標記在需要依賴的變數讓Dagger2為其提供依賴。
- @Provide 用Provide來標註一個方法,該方法可以在需要提供依賴時被調用,從而把預先提供好的對象當做依賴給標註了@Injection的變數賦值。provide主要用於標註Module里的方法
- @Module 用Module標註的類是專門用來提供依賴的。有的人可能有些疑惑,看了上面的@Inject,需要在構造函數上標記才能提供依賴,那麼如果我們需要提供 的類構造函數無法修改怎麼辦,比如一些jar包里的類,我們無法修改源碼。這時候就需要使用Module了。Module可以給不能修改源碼的類提供依 賴,當然,能用Inject標註的通過Module也可以提供依賴
- @Component Component一般用來標註介面,被標註了Component的介面在編譯時會產生相應的類的實例來作為提供依賴方和需要依賴方之間的橋梁,把相關依賴註入到其中。
看了這些概念我們回到剛纔的例子當中:
我們對User的構造函數進行了 @Inject的標註 意思就是告訴Dagger2 如果有誰要使用User這個類,我標註的這個構造函數,你可以直接用來實例化該類;
然後我們在Activity中對User也進行了@Inject的標註 意思是告訴Dagger2 這個類需要被註入,簡單的說就是 這個類我要用,你幫我實例化;
細心的讀者可能會發現 這樣會不會太簡單了,是的 是太簡單了不太正常,哈哈,上面的例子中還有一個標註@Component 光靠@Inject的標註是不足以完成註入的 我們需要用@Component來完成註入;
上例中被@Component標記的ActivityComponent介面就是一個註入器; void inject(MainActivity MainActivity);的意思是MainActivity中要用到這個註入器然後我們在MainActivity中對註入器進行初始化 DaggerActivityComponent.builder().build().inject(this); 然後Activity中所有被@Inject標記的類,都會通過ActivityComponent來進行初始化;
我們再把上例中的註入過程梳理一下:
1、首先定義一個類User 併在其構造函數用@Inject標註,表示告訴Dagger2這是我的構造函數,如果有地方要用到我,就用該構造函數對我實例化;
2、創建一個@Component標註的註入器介面,併在註入器中使用 void inject(MainActivity MainActivity);來表明哪裡要用到註入器;
這裡表示MainActivity中要用到該註入器
3、在MainActivity中對註入器進行初始化DaggerActivityComponent.builder().build().inject(this); 初始化後該註入器就可以正常使用了;
4、在MainActivity中對需要註入的類 User用@Inject進行標註,表示該類需要被註入,即實例化;
註意:在代碼編寫過程中 我們會發現DaggerActivityComponent會不存在,這是因為註入器是在編譯的過程中才生成,所以我們在對註入器編寫完成後
Make Project 一下就會生成DaggerActivityComponent
————————————————————————————————————————————————————————————————————————————————
現在我們已經明白了@InJect @Component的作用了,接下來我們來研究@Module和@Provide
通過上面的例子我們發現 @Inject是對類的構造函數進行標註來進行實例化的,但是有些類,比如第三方OkHttpClient,我們是無法對其源碼進行修改的
即對其構造函數進行標註,這個時候我們就用到了@Module
@Module是什麼意思呢 @Module是和@Component配合使用的 意思就是告訴註入器,如果你在實例化對象的時候,沒有找到合適的構造函數,你就來我這裡找,@Module通常標註一個類,該類裡面可以實例化各種類,Component在註入對象的時候先去Module中找,如果找不到就會檢查所有被@Inject標註的構造函數;所以我們可以把OkHttpClient放到Module中;
1 @Module 2 public class ActivityMoudle { 3 4 @Provides 5 @Singleton 6 OkHttpClient provideOkHttpClient() { 7 HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); 8 loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 9 10 Interceptor apikey = chain -> chain.proceed(chain.request().newBuilder() 11 .addHeader("apikey", Constants.Api_Key).build()); 12 13 OkHttpClient okHttpClient = new OkHttpClient.Builder() 14 .readTimeout(Constants.HTTP_CONNECT_TIMEOUT, TimeUnit.MILLISECONDS) 15 .connectTimeout(Constants.HTTP_CONNECT_TIMEOUT, TimeUnit.MILLISECONDS) 16 .addInterceptor(apikey) 17 .addInterceptor(loggingInterceptor) 18 .build(); 19 20 return okHttpClient; 21 } 22 23 }
以上代碼我們不需要知道是幹嘛的,我們只知道該類中的方法返回一個okHttpClient的實例;
一個被@Module標註的類用來返回一個okHttpClient的實例;
我們再來看一下在Component中的代碼:
1 @Singleton 2 @Component(modules = ActivityMoudle.class) 3 public interface ActivityComponent { 4 5 void inject(MainActivity MainActivity); 6 }
可以看到標註頭多了@Component(modules = ActivityMoudle.class),表示告訴註入器如果你要註入的類沒有找到構造函數,你就去ActivityMoudle.class中找
@Provide 用來標註一個方法,告訴註入器,我標註的方法你可以用來提供實例;
@Singleton 顧名思義,標註該實例化的對象為單例
然後我們在Activity直接標註使用就可以了
1 @Inject 2 OkHttpClient okHttpClient;
至此我們有兩種方式可以提供依賴,一個是註解了@Inject的構造方法,一個是在Module里提供的依賴,那麼Dagger2是怎麼選擇依賴提供的呢,規則是這樣的:
- 步驟1:查找Module中是否存在創建該類的方法。
- 步驟2:若存在創建類方法,查看該方法是否存在參數
- 步驟2.1:若存在參數,則按從步驟1開始依次初始化每個參數
- 步驟2.2:若不存在參數,則直接初始化該類實例,一次依賴註入到此結束
-
步驟3:若不存在創建類方法,則查找Inject註解的構造函數,看構造函數是否存在參數
-
步驟3.1:若存在參數,則從步驟1開始依次初始化每個參數
- 步驟3.2:若不存在參數,則直接初始化該類實例,一次依賴註入到此結束
這次依賴註入就先寫這麼多,Dagger結合MVP實現完美的配合,小伙伴們可以自己去研究一下其中的奧妙~