Android單排上王者系列之Dagger2使用解析

本篇文章已授權(quán)微信公眾號(hào) guolin_blog (郭霖)獨(dú)家發(fā)布

前言###

現(xiàn)在Dagger2在項(xiàng)目中的使用越來(lái)越多竖哩,Dagger2是Dagger的升級(jí)版本衔掸,Dagger沒(méi)有使用過(guò)咒钟,但是本篇說(shuō)的是Dagger2疏哗,主要講解的是Dagger2是如何使用的。對(duì)了庐镐,忘了說(shuō)Dagger其實(shí)是一個(gè)依賴注入的框架恩商。

什么是依賴注入###

依賴注入是一種面向?qū)ο蟮木幊棠J剑某霈F(xiàn)是為了降低耦合性必逆,所謂耦合就是類之間依賴關(guān)系怠堪,所謂降低耦合就是降低類和類之間依賴關(guān)系∶迹可能有的人說(shuō)自己之前并沒(méi)有使用過(guò)依賴注入粟矿,其實(shí)真的沒(méi)有使用過(guò)嗎?當(dāng)我們?cè)谝粋€(gè)類的構(gòu)造函數(shù)中通過(guò)參數(shù)引入另一個(gè)類的對(duì)象损拢,或者通過(guò)set方法設(shè)置一個(gè)類的對(duì)象其實(shí)就是使用的依賴注入陌粹。

通常依賴注入有以下幾種方式###

  • 通過(guò)接口注入
interface ClassBInterface { 
        void setB(ClassB b);
}
public class ClassA implements ClassBInterface { 
        ClassB classB; 
        @override 
        void setB(ClassB b) { 
                classB = b; 
        }
}
  • 通過(guò)set方法注入
public class ClassA { 
        ClassB classB;  
        public void setClassB(ClassB b) { 
                classB = b; 
        }
}
  • 通過(guò)構(gòu)造方法注入
public class ClassA { 
        ClassB classB; 
        public void ClassA(ClassB b) { 
                classB = b; 
        }
}
  • 通過(guò)注解的方式注入
public class ClassA { 
//此時(shí)并不會(huì)完成注入,還需要依賴注入框架的支持福压,如Dagger2 
        @inject  
        ClassB classB; 
        public ClassA() {
        }
}

下面我們就來(lái)說(shuō)說(shuō)如何通過(guò)Dagger2來(lái)實(shí)現(xiàn)依賴注入吧掏秩。

引入Dagger2

添加apt插件

dependencies { 
        classpath 'com.android.tools.build:gradle:2.1.2' //添加apt插件 
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' 
}

添加依賴(在build.gradle中添加如下代碼)

apply plugin: 'com.android.application' //添加如下代碼,應(yīng)用apt插件 
apply plugin: 'com.neenbedankt.android-apt'
... 
dependencies {
        ...
        compile 'com.google.dagger:dagger:2.4' apt 'com.google.dagger:dagger-compiler:2.4' //java注解 
        compile 'org.glassfish:javax.annotation:10.0-b28' 
       ...
}

使用Dagger2

添加完Dagger的依賴后我們?nèi)绾卧陧?xiàng)目中使用Dagger呢荆姆?
在項(xiàng)目中絕大多數(shù)的使用都是Dagger結(jié)合MVP架構(gòu)使用的蒙幻,在MVP中使用是非常典型的降低耦合的使用。不懂MVP的可以看這里胆筒。
本篇文章中的示例是一個(gè)簡(jiǎn)單的登陸功能的示例邮破,代碼沿用上篇講解MVP的登陸代碼,看這里腐泻,該示例采用MVP架構(gòu)設(shè)計(jì)通過(guò)Dagger2進(jìn)行解耦合决乎,下面就來(lái)看看如何使用吧队询。
在使用Dagger2前我們最好簡(jiǎn)單的了解一下MVP派桩,主要是為了理解本篇中的代碼。簡(jiǎn)單了解MVP即使不會(huì)寫(xiě)MVP也可以看的懂本篇的代碼蚌斩。
為什么要選擇在MVP模式中使用Dagger2呢铆惑?
因?yàn)樵贛VP模式中Activity持有presenter的引用,同時(shí)presenter也持有view的引用,這樣便于更新UI界面员魏,這樣Activity就和presenter僅僅的耦合在一起了丑蛤,而Dagger2是依賴注入框架就是解耦合的,所以子MVP中使用Dagger2也就再好不過(guò)了撕阎。
在上篇文章講解MVP時(shí)我們可以明顯的看到如下代碼

public class LoginActivity extends AppCompatActivity implements ILoginView,View.OnClickListener{ 
        private Button mLogin ; 
        private Button mClear ; 
        private EditText mName ; 
        private EditText mPassWord ; 
        ILoginPresenter loginPresenter ; 
        @Override 
        protected void onCreate(Bundle savedInstanceState) { 
                super.onCreate(savedInstanceState); 
                setContentView(R.layout.activity_main); 
                mLogin = (Button) findViewById(R.id.btn_login); 
                mClear = (Button) findViewById(R.id.btn_clear); 
                mName = (EditText) findViewById(R.id.et_name); 
                mPassWord = (EditText) findViewById(R.id.et_password); 
                mLogin.setOnClickListener(this); 
                mClear.setOnClickListener(this); //持有presenter的引用并且創(chuàng)建對(duì)象 
                loginPresenter = new LoginPresenterCompl(this) ; 
        }
         ........
}

在上述代碼中可以看到activity持有了presenter的引用并且創(chuàng)建了該對(duì)象受裹,但是如果presenter的構(gòu)造函數(shù)發(fā)生改變則這里也需要改變,其實(shí)所有和presenter構(gòu)造函數(shù)相關(guān)的代碼都要改變虏束。
但是如果我們使用Dagger2依賴框架該如何使用呢棉饶?
請(qǐng)看下面代碼activity中的代碼

public class LoginActivity extends AppCompatActivity implements ILoginView,View.OnClickListener{ 
        .......... 
        //注意此處使用了注解 
        @Inject LoginPresenterCompl loginPresenter ; 
        @Override 
        protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState); 
                setContentView(R.layout.activity_main); 
                mLogin = (Button) findViewById(R.id.btn_login); 
                mClear = (Button) findViewById(R.id.btn_clear); 
                mName = (EditText) findViewById(R.id.et_name); 
                mPassWord = (EditText) findViewById(R.id.et_password); 
                mLogin.setOnClickListener(this); 
                mClear.setOnClickListener(this); 
                DaggerMainComponent.builder().mainModule(new MainModule(this)).build().inject(this); 
        }
       .......
}

LoginPresenterCompl中的代碼

public class LoginPresenterCompl implements ILoginPresenter { 
        private ILoginView loginView ; 
        private User user ;  
        //注意此處使用了注解 
        @Inject public LoginPresenterCompl(ILoginView view){ 
                loginView = view ; 
                user = new User("張三","123456") ; 
        }
         ...... 
}

只有上述兩個(gè)注解還無(wú)法完成依賴注入,還需要如下兩個(gè)新增類新增的MainModule類

@Modulepublic 
class MainModule { 
        private final ILoginView view ; 
        public MainModule(ILoginView view){ 
                this.view = view ; 
        } 
        @Provides 
        ILoginView provideILogView(){ 
                return view ; 
        }
}

新增的MainComponent接口

@Component(modules = MainModule.class)
public interface MainComponent { 
        public void inject(LoginActivity activity) ;
}

通過(guò)直接注解和上述兩個(gè)接口類即可完成Dagger2的依賴注入镇匀。在LoginActivity中是通過(guò)

DaggerMainComponent.builder().mainModule(new MainModule(this)).build().inject(this)

完成依賴注入的照藻。看完上面的代碼后汗侵,一臉的懵逼幸缕,WTF(what the fuck),這TM是什么晰韵,這么復(fù)雜发乔,還不如之前的簡(jiǎn)單呢,新增了兩個(gè)類還有這么多代碼雪猪,得不償失呀列疗!
同志們,如果你們第一眼看到后是這樣想的話浪蹂,說(shuō)明和我想的一樣抵栈,呵呵。每一個(gè)剛接觸Dagger2的人可能都會(huì)這樣想坤次,因?yàn)槲覀冎豢吹搅吮砻妗?br> 不錯(cuò)古劲,表面上我們是多了一個(gè)類和接口也多了很多代碼,但是這樣的組合其實(shí)是可以理解的缰猴。因?yàn)橥ǔ:?jiǎn)單的代碼具有耦合性产艾,而要想降低這樣的耦合就需要其他的輔助代碼,其實(shí)少代碼量和低耦合這兩者并不能同時(shí)兼顧滑绒,古人云:魚(yú)和熊掌不可兼得闷堡。我們作為堂堂聰明絕頂?shù)某绦蛟吃趺纯赡軙?huì)輸給古人呢。
好疑故!下面來(lái)認(rèn)真講解Dagger2是如何完成依賴注入的杠览。
首先我們來(lái)看看LoginActivity代碼LoginActivity中有這么一段代碼

@Inject
LoginPresenterCompl loginPresenter ;

同樣在LoginPresenterCompl中也有這么一段代碼

@Inject
public LoginPresenterCompl(ILoginView view){ 
        loginView = view ; 
        user = new User("張三","123456") ;
}

之所以挑出這兩段代碼是因?yàn)樗鼈兌继砑恿薂Inject注解。
在LoginActivity中其實(shí)只有這么一句提到loginPresenter纵势,在接下來(lái)的代碼中并沒(méi)有對(duì)其進(jìn)行初始化踱阿。那loginPresenter是如何進(jìn)行初始化的呢(此處注意添加@Inject注解的變量不能被private修飾)管钳?
直觀上我們可以這樣理解,被@Inject注解的代碼存在某種聯(lián)系软舌,當(dāng)代碼執(zhí)行到@Inject的時(shí)候程序會(huì)自動(dòng)進(jìn)入到這個(gè)類的構(gòu)造方法中才漆,如果正巧這個(gè)構(gòu)造方法也被@Inject修飾了,那么系統(tǒng)就會(huì)幫我們自動(dòng)創(chuàng)建對(duì)象佛点。
這只是表面的理解醇滥,這其中肯定還有很多我們沒(méi)有看到的“貓膩”。這倆不會(huì)無(wú)緣無(wú)故的有聯(lián)系超营,肯定還有第三者腺办,通過(guò)這個(gè)第三者這兩個(gè)被@Inject注解修飾的代碼才會(huì)產(chǎn)生聯(lián)系。
這個(gè)第三者是誰(shuí)呢糟描?
自然的我們就會(huì)想到我們添加的這個(gè)類和接口怀喉。
首先我們來(lái)分析MainComponent接口代碼如下

@Component(modules = MainModule.class)
public interface MainComponent { 
        public void inject(LoginActivity activity) ;
}

MainComponent是一個(gè)接口(也可以是一個(gè)抽象類),在這個(gè)接口中我們定義了一個(gè)inject()方法船响,其中參數(shù)是LoginActivity對(duì)象躬拢,同時(shí)MainComponent還被@Component注解著,注解中modules的值是MainModule.class见间,這個(gè)內(nèi)容會(huì)在接下來(lái)的地方進(jìn)行說(shuō)明聊闯,暫時(shí)先放一放。
此時(shí)在Android studio中米诉,如果我們r(jià)ebuild的一下項(xiàng)目就會(huì)有新的發(fā)現(xiàn)菱蔬。在項(xiàng)目的build/generated/source/apt/debug/項(xiàng)目包名/dragger目錄下生成對(duì)應(yīng)的包其中包含DaggerMainComponent類,這個(gè)類名其實(shí)不是固定的史侣,是根據(jù)我們上面寫(xiě)的MainComponent拴泌,加了Dagger前綴生成的DaggerMainComponent。其實(shí)在這個(gè)時(shí)候我們就已經(jīng)完成了present的依賴注入惊橱。但是在

DaggerMainComponent.builder().mainModule(new MainModule(this)).build().inject(this)

中我們看到還有一個(gè)MainModule蚪腐,這個(gè)是我們自己創(chuàng)建的一個(gè)類MainModule代碼如下

@Modulepublic 
class MainModule { 
       private final ILoginView view ; 
       public MainModule(ILoginView view){ 
               this.view = view ; 
       } 
       @Provides 
       ILoginView provideILogView(){ 
               return view ; 
       }
}

我們可以看到這個(gè)類被@Module注解修飾,內(nèi)部有一個(gè)ILoginView的變量和一個(gè)構(gòu)造方法還有一個(gè)被@Provides修飾的provideILogView方法税朴。
看到這還是一臉懵逼回季,這個(gè)類是干嘛的?
在MainComponent接口中我們看到這么一個(gè)注解@Component(modules = MainModule.class)正林,這里用到了MainModule泡一,可見(jiàn)MainComponent需要MainModule一起才能完成工作。其實(shí)這個(gè)類我們可以理解成提供參數(shù)的觅廓,也就是提供參數(shù)依賴的鼻忠,如何理解呢?
在MainModule中我們?yōu)槭裁匆峁㊣LoginView類型的對(duì)象哪亿?為什么不是其他的呢粥烁?這是因?yàn)長(zhǎng)oginPresenterCompl的構(gòu)造函數(shù)需要這么一個(gè)參數(shù),所以我們?cè)谶@里提供這么一個(gè)相同的參數(shù)蝇棉,并通過(guò)被@Provides注解修飾的方法將其返回出去讨阻,如果LoginPresenterCompl還需要其他的參數(shù),同樣我們也可以在這里添加對(duì)應(yīng)類型的參數(shù)然后通過(guò)另一個(gè)被@Provides注解修飾的方法返回出去篡殷。在MainComponent接口中提供的inject()方法的參數(shù)是LoginActivity钝吮,這個(gè)參數(shù)的含義是LoginPresenter要在什么地方注入厘贼。

了解了各個(gè)類的功能后我們來(lái)總結(jié)一下

  • @Inject 程序會(huì)將Dagger2會(huì)將帶有此注解的變量或者構(gòu)造方法參與到依賴注入當(dāng)中漆羔,Dagger2會(huì)實(shí)例化這個(gè)對(duì)象- @Module 帶有該注解的類需要對(duì)外提供依賴,其實(shí)就是提供實(shí)例化需要的參數(shù)验靡,Dagger2在實(shí)例化的過(guò)程中發(fā)現(xiàn)一些參數(shù)劲弦,Dagger2就會(huì)到該類中尋找?guī)в蠤Provides注解的以provide開(kāi)頭的需找對(duì)應(yīng)的參數(shù)
  • @Component 帶有該注解的接口或抽象類起到一個(gè)關(guān)聯(lián)橋梁的作用耳标,作用就是將帶有@Inject的方法或?qū)ο蠛蛶в蠤Module的類進(jìn)行關(guān)聯(lián),只有通過(guò)該接口或抽象類才可以在實(shí)例化的時(shí)候到帶有@Module中類中去尋找需要的參數(shù)邑跪,也就是依賴注入次坡。

OK,下面我們來(lái)捋捋思路画畅。

  • 1砸琅、在這個(gè)示例代碼中,LoginActivity中需要LoginPresenterCompl轴踱,所以在LoginActivity中定義了該對(duì)象并且通過(guò)@Inject將其注解症脂,同時(shí)到LoginPresenterCompl的構(gòu)造方法中也通過(guò)@Inject將其注解,表明這些是需要依賴注入的淫僻。
  • 2诱篷、因?yàn)樵贚oginPresenterCompl的構(gòu)造方法需要ILoginView類型的參數(shù),所以需要通過(guò)依賴將獲取這些參數(shù)雳灵,所以就需要帶有@Module注解的類用于獲取需要的參數(shù)兴蒸,在@Module注解的類中通過(guò)被@Provides注解的以provide開(kāi)頭的方法對(duì)外提供需要的參數(shù),一般而言有幾個(gè)參數(shù)就需要有幾個(gè)帶有@Provides的方法细办。
  • 3橙凳、此時(shí)還需要一個(gè)橋梁將兩者聯(lián)系到一起,帶有@Component的接口或抽象類就起到這個(gè)橋梁的作用笑撞。注解中有一個(gè)module的值岛啸,這個(gè)值指向需要依賴的Module類,同時(shí)其中有一個(gè)抽象方法inject()茴肥,其中的參數(shù)就是我們需要在哪個(gè)類中實(shí)例化LoginPreserentCompl坚踩,因?yàn)槲覀冃枰贚oginActivity中實(shí)例化,所以參數(shù)類型就是LoginActivity類型瓤狐。然后在Android studio中rebuild我們的項(xiàng)目瞬铸,就會(huì)生成DaggerMainComponent類批幌,通過(guò)
DaggerMainComponent.builder().mainModule(new MainModule(this)).build().inject(this);

完成我們需要的依賴注入。###總結(jié)可能我們通過(guò)上面的講解嗓节,知道了如何使用Dagger2了荧缘,也知道具體的流程了,但是可能還會(huì)有些疑惑拦宣,為什么截粗?Dagger2是如何通過(guò)一些接口和類就完成依賴注入的?在此聲明鸵隧,別著急绸罗,知道如何使用這只是第一步,在下一篇文章中將會(huì)講解Dagger2實(shí)現(xiàn)依賴注入的原理豆瘫。敬請(qǐng)期待I后啊!外驱!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末系洛,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子略步,更是在濱河造成了極大的恐慌描扯,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趟薄,死亡現(xiàn)場(chǎng)離奇詭異绽诚,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)杭煎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門恩够,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人羡铲,你說(shuō)我怎么就攤上這事蜂桶。” “怎么了也切?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵扑媚,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我雷恃,道長(zhǎng)疆股,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任倒槐,我火速辦了婚禮旬痹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己两残,他們只是感情好永毅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著人弓,像睡著了一般沼死。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上票从,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天漫雕,我揣著相機(jī)與錄音滨嘱,去河邊找鬼峰鄙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛太雨,可吹牛的內(nèi)容都是我干的吟榴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼囊扳,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼吩翻!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起锥咸,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤狭瞎,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后搏予,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體熊锭,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年雪侥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了碗殷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡速缨,死狀恐怖锌妻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情旬牲,我是刑警寧澤仿粹,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站原茅,受9級(jí)特大地震影響牍陌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜员咽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一毒涧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦契讲、人聲如沸仿吞。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)唤冈。三九已至,卻和暖如春银伟,著一層夾襖步出監(jiān)牢的瞬間你虹,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工彤避, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留傅物,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓琉预,卻偏偏與公主長(zhǎng)得像董饰,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子圆米,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容