用Dagger2在Android中實(shí)現(xiàn)依賴(lài)注入
依賴(lài)注入這個(gè)模式(模式已經(jīng)用爛了,這里再爛一次)是用來(lái)給應(yīng)用的各部分解耦的。使應(yīng)用開(kāi)發(fā)更加可擴(kuò)展,更容易維護(hù)。通過(guò)本文你會(huì)學(xué)到如何使用Dagger2來(lái)處理依賴(lài)。
簡(jiǎn)介
如果以對(duì)象需要另外的一個(gè)對(duì)象才能完成一個(gè)完整功能的話(huà),那么這里就存在一個(gè)依賴(lài)。比如,悟空要用金箍棒才能三打白骨精,要筋斗云才能十萬(wàn)八千里。悟空有對(duì)金箍棒和筋斗云的依賴(lài)。你可以在悟空對(duì)象里初始化金箍棒,也可以用一個(gè)工廠方法批量生產(chǎn)金箍棒。使用依賴(lài)注入可以無(wú)需一個(gè)專(zhuān)門(mén)的類(lèi)來(lái)初始化這些依賴(lài)對(duì)象。這樣就實(shí)現(xiàn)了解耦。
本教程會(huì)使用***的Dagger2(當(dāng)前的版本是2.2)。這里是官網(wǎng)。你可以在這里找到***的發(fā)布。
準(zhǔn)備
Android Studio是必須的。其他:
1. Dagger2 基礎(chǔ)
注解講解:
- @Module這個(gè)annotation修飾的類(lèi)專(zhuān)門(mén)用來(lái)提供依賴(lài)
- @Provides這個(gè)annotation修飾的方法用在Module類(lèi)里
- @Inject用來(lái)annotation一個(gè)依賴(lài)(可以是構(gòu)造方法、field或者一般的方法)
- @Component連接@Module和注入的橋梁
這些名詞看起來(lái)非常抽象。下面稍微解釋一下。依賴(lài)反射并沒(méi)有什么神奇的地方。只不過(guò)是我們需要單獨(dú)寫(xiě)初始化依賴(lài)的地方由其他的框架代替了。這個(gè)依賴(lài)關(guān)系也有我們常寫(xiě)的代碼轉(zhuǎn)移到了“配置文件”中。
在很久以前,依賴(lài)注入的框架就是這樣處理依賴(lài)注入的:讀取配置文件的依賴(lài)關(guān)系,然后用反射的方法初始化被依賴(lài)對(duì)象并賦值給調(diào)用依賴(lài)的對(duì)象。比如,我們之前在悟空類(lèi)中初始化金箍棒:
- public class Wukong {
- private Jingubang jingubang;
- public Wukong(){
- // 依賴(lài)
- this.jingubang = Jingubang();
- }
- }
后來(lái)有了使用配置文件的依賴(lài)注入(這里都是虛構(gòu)的文件格式):
- <xml>
- <com.xiyou.Wukong>
- <dependency field="jingubang">
- <com.xiyou.Jingubang />
- </dependency>
- </com.xiyou.Wukong>
- </xml>
在悟空使用金箍棒的時(shí)候,依賴(lài)注入框架自動(dòng)初始化好了金箍棒,并賦值給了悟空。
現(xiàn)在使用Dagger2。這里就有不得不說(shuō)的牛X的地方了。因?yàn)槭窃贏ndroid里能用的資源沒(méi)有后端那么多。尤其反射消耗比較大!所以Dagger為了滿(mǎn)足移動(dòng)開(kāi)發(fā)節(jié)約資源的需要,沒(méi)有使用反射實(shí)現(xiàn)依賴(lài)注入。而是在編譯的時(shí)候同時(shí)生成依賴(lài)注入的相關(guān)代碼。生成代碼的根據(jù)就是前文中說(shuō)明的那些注解(annotation)以及使用這些annotation的類(lèi)、接口。
總結(jié)起來(lái)就一句話(huà),Dagger把你需要在悟空類(lèi)里寫(xiě)的金箍棒類(lèi)的初始化代碼都根據(jù)注解替你自動(dòng)生成了!只不過(guò)這種生成的代碼比明晃晃的使用new初始化的方法更加復(fù)雜一些。
Dagger2 開(kāi)發(fā)步驟
把大象裝冰箱一共分幾步:
- 定義依賴(lài)和被依賴(lài)的對(duì)象的類(lèi),悟空類(lèi)和金箍棒類(lèi)。“悟空類(lèi)”和“金箍棒類(lèi)”的構(gòu)造函數(shù)用@Inject注解修飾。
- 定義一個(gè)@Module注解的類(lèi),一般叫做XXXModule。里面寫(xiě)的@Provides注解修飾的方法。這些@Provides方法返回“悟空類(lèi)”和“金箍棒類(lèi)”對(duì)象。比如@Provides Wukong provideWukong(){ return new Wukong(); }
- 創(chuàng)建一個(gè)interface,并用@Component注解修飾。一般叫做XXXComponent。里面寫(xiě)一個(gè)注入方法:void inject(Wukong wk);。這里Wukong只是一個(gè)例子。任何你準(zhǔn)備要注入的類(lèi)都可以代替上面參數(shù)的Wukong類(lèi)。
- 在需要注入的地方寫(xiě)@Inject的field。
***,Dagger會(huì)根據(jù)上面的內(nèi)容和***的@Component接口生成一個(gè)DaggerXXXComponent的類(lèi)型,使用這個(gè)類(lèi)型來(lái)實(shí)現(xiàn)注入。上面的1到3步可以理解為依賴(lài)的配置。***的XXXComponent代替古老的Reflect方式實(shí)現(xiàn)注入。
***步的@Inject修飾的構(gòu)造函數(shù)和`@Module`的`provideXXX`方法二者可以省略一個(gè)。
Dagger可以根據(jù)其中的任意一種配置創(chuàng)建依賴(lài)的對(duì)象。都寫(xiě)上等于有了雙保險(xiǎn)。
上文提到過(guò)多次。Dagger 2厲害的地方就在于這個(gè)庫(kù)完全不用反射,而是用在編譯期生成代碼的方式實(shí)現(xiàn)的依賴(lài)注入。這個(gè)特點(diǎn)導(dǎo)致在Android Studio配置的時(shí)候需要做一些額外的工作。
這里假設(shè)你已經(jīng)創(chuàng)建了一個(gè)新的Android應(yīng)用項(xiàng)目。下面打開(kāi)build.gradle文件,我們一步一步的來(lái)完成Dagger2的配置。
3. Android Studio的配置
***步
- apply plugin: 'kotlin-android' // 非必須
- apply plugin: 'kotlin-android-extensions' // 必須!!!
為什么要加一個(gè)新的plugin呢?這個(gè)是為后面使用的kapt和provided提供支持的。gradle本身不支持這兩個(gè)操作。
第二步
- buildscript {
- ext.kotlin_version = '1.0.1-2'
- repositories {
- mavenCentral()
- }
- dependencies {
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
- }
- }
第三步
- dependencies {
- // ...其他略...
- compile 'com.google.dagger:dagger:2.2'
- kapt 'com.google.dagger:dagger-compiler:2.2'
- provided 'javax.annotation:jsr250-api:1.0'
- }
- dagger, 我們要用的正主。
- dagger-compiler, 用來(lái)生成代碼。
- java.annotation, 提供Dagger意外的注解
***,同步Gradle。
使用Dagger 2
下面就是Dagger一展身手的時(shí)候了。
首先,我們已經(jīng)有悟空和金箍棒了。代碼如下:
悟空:
- import javax.inject.Inject;
- /**
- * Created by uncle_charlie on 6/4/2016.
- */
- public class Wukong {
- @Inject
- JinGuBang jinGuBang;
- @Inject
- public Wukong() {
- }
- public String useJinGuBang() {
- return this.jinGuBang.use();
- }
- }
金箍棒:
- import javax.inject.Inject;
- /**
- * Created by uncle_charlie on 6/4/2016.
- */
- public class JinGuBang {
- @Inject
- public JinGuBang() {
- }
- public String use() {
- return "user Jing gu bang";
- }
- }
悟空對(duì)金箍棒有依賴(lài),所以金箍棒屬性有@Inject注解修飾。
因?yàn)閮蓚€(gè)類(lèi)都需要Dagger創(chuàng)建,所以在構(gòu)造函數(shù)上都有@Inject注解。
第二步 創(chuàng)建@Module類(lèi)
創(chuàng)建@Module注解的類(lèi),并在其中添加@Provides注解修飾的方法。這些方法創(chuàng)建被依賴(lài)的對(duì)象。
- import dagger.Module;
- import dagger.Provides;
- /**
- * Created by uncle_charlie on 6/4/2016.
- */
- @Module
- public class XiYouModule {
- @Provides
- // @Singleton
- Wukong provideWukong() {
- return new Wukong();
- }
- @Provides
- // @Singleton
- JinGuBang provideJinGuBang() {
- return new JinGuBang();
- }
- }
- @Singleton注解表明,這個(gè)被依賴(lài)的對(duì)象在應(yīng)用的生命周期里只有一個(gè)實(shí)例。
- 這個(gè)里的@Provides方法和前一步說(shuō)到的@Inject注解的構(gòu)造函數(shù)兩個(gè)可以只寫(xiě)一個(gè)。
第三步 @Component接口,連接@Module和@Inject
@Module和@Provides方法提供了被依賴(lài)的對(duì)象。@Inject在@Component接口出現(xiàn)的地方則是指明了需要注入的地方(一般是一個(gè)field)。@Component接口就是用來(lái)把他們連接起來(lái)的。
- import android.app.Activity;
- import javax.inject.Singleton;
- import dagger.Component;
- /**
- * Created by uncle_charlie on 6/4/2016.
- */
- @Component(modules = {XiYouModule.class})
- @Singleton
- public interface XiYouComponent {
- void inject(Wukong wk);
- void inject(Activity a);
- }
其中inject()方法里使用的對(duì)象,就是包含@Inject的field的需要注入的對(duì)象。
在這個(gè)接口中也可以不用inject()方法,而使用provideXXX()方法后面會(huì)有更多介紹。
注意:@Component接口一定要在直接中指明@Module類(lèi)型
第四步 使用@Component接口獲取對(duì)象
經(jīng)過(guò)前面的步驟,依賴(lài)和被依賴(lài)對(duì)象關(guān)系都已經(jīng)配置好了。下面就來(lái)獲取被依賴(lài)對(duì)象來(lái)注入依賴(lài)對(duì)象。
- public class MainActivity extends AppCompatActivity {
- private static final String TAG = "##MainActivity";
- @Inject
- Wukong wukong;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- TextView welcomeTextView = (TextView) findViewById(R.id.welcome_textview);
- // 1
- XiYouComponent xiYouComponent = DaggerXiYouComponent
- .builder()
- // 2
- .xiYouModule(new XiYouModule())
- .build();
- xiYouComponent.inject(this);
- // 3
- welcomeTextView.setText(wukong.useJinGuBang());
- }
- }
首先主要到屬性@Inject Wukong wukong;已經(jīng)在MainActivity 聲明了。這里表明一個(gè)依賴(lài)關(guān)系:這個(gè)activity依賴(lài)于悟空,并準(zhǔn)備注入悟空對(duì)象。
- Dagger2會(huì)在編譯器自動(dòng)生成依賴(lài)注入的代碼,所以在添加上面的代碼之前需要編譯一下。DaggerXiYouComponent就是Dagger根據(jù)我們的XiYouModule類(lèi)生成的代碼。
- 在這一步給DaggerXiYouComponent的builder添加X(jué)iYouModule的實(shí)例。如果這個(gè)Module只需要用到無(wú)參構(gòu)造函數(shù)的話(huà)可以用一種省略用法:create()方法。可以簡(jiǎn)寫(xiě)為:
- DaggerXiYouComponent
- .builder()
- // 2
- //.xiYouModule(new XiYouModule())
- //.build()
- .create();
Component接口的對(duì)象調(diào)用inject(this)方法之后注入即完成。所以可以直接使用@Inject Wukong wukong;屬性來(lái)調(diào)用方法:welcomeTextView.setText(wukong.useJinGuBang());***在activity中顯示方法返回的文字。
運(yùn)行代碼,看看結(jié)果吧。
結(jié)論
以上內(nèi)容可以概括為:什么被依賴(lài),就把什么放在@Module類(lèi)里(或者什么被依賴(lài),就給什么添加@Inject的無(wú)參構(gòu)造函數(shù))。什么有依賴(lài)(@Inject屬性),就把什么放在@Component接口的inject()方法參數(shù)里。(或者有什么@Inject屬性,就在@Component接口里provide什么對(duì)象)。這個(gè)概括不一定嚴(yán)密,但是基本用法全部包括了。
依賴(lài)注入是很有用的。以上的內(nèi)容只是Dagger2依賴(lài)注入的一部分。各位讀者還需要根據(jù)官方文檔多加練習(xí)才能更好的理解依賴(lài)注入和Dagger的各種用法。