本篇文章已授權(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后啊!外驱!