Dagger2 Android依賴(lài)注入學(xué)習(xí)筆記
前言
最近在用 MVP + RxJava + Retrofit 寫(xiě)項(xiàng)目,覺(jué)得相對(duì)于其他的開(kāi)發(fā)框架,這的確是給我們帶來(lái)了很多方便,但是在網(wǎng)上搜尋相關(guān)資料的時(shí)候,總是能看到 MVP + RxJava + Retrofit + Dagger 這樣的搭配組合,那 Dagger 又是一個(gè)怎樣的框架呢,我也去具體搜了搜,但看到一些文章帶著“Dagger2從入門(mén)到放棄”這樣意思的句子,就感覺(jué)Dagger2會(huì)很難嗎,emmmm...行吧,好像是有點(diǎn)難理解,但是想著既然有那么多人用這個(gè)框架,必然有它的好處,于是花了些時(shí)間學(xué)習(xí)了一波。
Dagger2
Dagger2 : A fast dependency injector for Android and Java. (官方給出的定義)
Dagger2是一個(gè)依賴(lài)注解框架,它的作用就是通過(guò)注解來(lái)實(shí)現(xiàn)組件之間的依賴(lài),由此來(lái)達(dá)到更好的解耦,比如說(shuō),如果將 Dagger2 使用到我們的 MVP 框架中,那么我們就可以達(dá)到 V層 和 P層 的進(jìn)一步解耦,從而使我們的項(xiàng)目有更好的維護(hù)性。
Dagger2 和 Butterknife 一樣,都是在編譯階段利用Java注解通過(guò)APT(Annotation Processing Tool)自動(dòng)生成Java代碼,然后由我們自己寫(xiě)的代碼進(jìn)行調(diào)用,完成依賴(lài)注入,因此不用擔(dān)心性能上的問(wèn)題。那么如何來(lái)使用 Dagger2 呢?我們先來(lái)看一下使用 Dagger2 和不使用的區(qū)別。
對(duì)比
用簡(jiǎn)單的例子來(lái)說(shuō)明。比如說(shuō)我們有這樣一個(gè)AAA類(lèi),沒(méi)什么內(nèi)容,只有一個(gè)get()方法
/** * @author chaochaowu * @Description : AAA * @class : AAA * @time Create at 6/12/2018 10:58 AM */ public class AAA { public AAA() { } public void get(){ Log.e("AAA","has gotten."); } }
我們需要在MainActivity用到這個(gè)類(lèi),因此MainActivity中變含有這個(gè)成員變量,有了這個(gè)成員變量aaa,我們就要對(duì)它進(jìn)行實(shí)例化,因此會(huì)有以下的代碼:
/** * @author chaochaowu * @Description : MainActivity * @class : MainActivity * @time Create at 6/12/2018 10:58 AM */ public class MainActivity extends AppCompatActivity { AAA aaa; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); aaa = new AAA(); aaa.get(); } }
我們需要在 MainActivity 中聲明aaa這個(gè)變量,然后會(huì)對(duì)其進(jìn)行new一個(gè)實(shí)例,然后才能對(duì)其進(jìn)行調(diào)用,調(diào)用它定義的方法。這樣,AAA類(lèi)對(duì)象的創(chuàng)建就和 MainActivity 耦合在了一起。如果使用 Dagger2 進(jìn)行依賴(lài)注入呢
/** * @author chaochaowu * @Description : MainActivity * @class : MainActivity * @time Create at 6/12/2018 10:58 AM */ public class MainActivity extends AppCompatActivity { @Inject AAA aaa; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); DaggerMainActivityComponent.create().inject(this); aaa.get(); } }
代碼就會(huì)變成這樣。我們可以發(fā)現(xiàn)MainActivity里沒(méi)有任何有關(guān)aaa的初始化賦值,但是它還是可以調(diào)用其get()方法。這樣我們就達(dá)到了解耦合的目的。
這樣一個(gè)簡(jiǎn)單的例子可能看不出 Dagger2 的好處,但是你可以想象一下,如果這個(gè) Activity 里有很多個(gè)類(lèi)似 AAA 這樣的類(lèi)的成員變量,它們都需要初始化,有的初始化還要傳參數(shù),那么,這個(gè) Activity 就會(huì)變得臃腫,如果 AAA類(lèi) 的初始化參數(shù)變了,不僅 AAA類(lèi) 需要改,MainActivity 因?yàn)樗cAAA類(lèi)的耦合也要跟著改變,如果 AAA類(lèi) 不止用在 MainActivity 中還用在了其他的 Activity 中,那么要改的東西變的可有點(diǎn)多咯,但是如果用上Dagger2,我們就可以很好的解決這個(gè)問(wèn)題。
注解介紹
Dagger2中注解有:@Inject, @Component, @Module, @Provides, @Qulifier, @Scope, @Singleten 。而我們主要用到的是@Inject, @Component,@Module, @Provides 這四個(gè),下面來(lái)介紹一下。
@Inject
Inject 主要用來(lái)標(biāo)記需要依賴(lài)的變量,告訴Dagger需要為它提供依賴(lài);inject 還被用來(lái)標(biāo)記類(lèi)的構(gòu)造函數(shù)。當(dāng)Dagger2碰到使用@Inject注解的變量時(shí),會(huì)去尋找這個(gè)變量對(duì)應(yīng)的被@Inject注解的構(gòu)造函數(shù),把實(shí)例構(gòu)造出來(lái),為變量提供依賴(lài)。
@Component
@Component用于標(biāo)注接口,是依賴(lài)需求方(MainActivity)和依賴(lài)提供方(AAA)之間的橋梁。被Component標(biāo)注的接口在編譯時(shí)會(huì)生成該接口的實(shí)現(xiàn)類(lèi),類(lèi)的名稱(chēng)為被@Component注解的類(lèi)的名稱(chēng)前加上Dagger,我們通過(guò)調(diào)用這個(gè)實(shí)現(xiàn)類(lèi)的方法來(lái)完成注入。
@Module
Module用于標(biāo)注提供依賴(lài)的類(lèi)。雖然我們有@Inject注解實(shí)現(xiàn)注入,但是@Inject只能注入構(gòu)造函數(shù)為無(wú)參的類(lèi),要是構(gòu)造函數(shù)是帶參數(shù)的呢?那就要使用module注解來(lái)解決這個(gè)問(wèn)題,又比如說(shuō),有很多的第三方庫(kù),我們對(duì)它的代碼無(wú)法進(jìn)行修改,也就不能對(duì)其構(gòu)造函數(shù)加上@Inject注解,那么可咋辦啊,@module注釋可以很好的解決我們的問(wèn)題。
@Provides
Provides是與Module一起使用的,@Provides用于標(biāo)注Module所標(biāo)注的類(lèi)中的方法,該方法會(huì)在需要提供依賴(lài)時(shí)被調(diào)用,在方法內(nèi)進(jìn)行對(duì)象的初始化,返回對(duì)象依賴(lài)給標(biāo)注了@Inject的變量。相當(dāng)于一個(gè)有參數(shù)的@Inject。
我們來(lái)具體的使用一下。
構(gòu)造函數(shù)無(wú)參的對(duì)象注入
用上面提到的 AAA類(lèi) 進(jìn)行依賴(lài)注入演示。
首先我們?cè)?AAA類(lèi) 的構(gòu)造函數(shù)上加上 @Inject 注解(構(gòu)造函數(shù)必須得是無(wú)參)
/** * @author chaochaowu * @Description : AAA * @class : AAA * @time Create at 6/12/2018 10:58 AM */ public class AAA { @Inject public AAA() { } public void get(){ Log.e("AAA","has gotten."); } }
然后創(chuàng)建一個(gè) MainActivityComponent 接口并用 @Component 進(jìn)行注解,然后Build一下項(xiàng)目,
/** * @author chaochaowu * @Description : MainActivityComponent * @class : MainActivityComponent * @time Create at 6/12/2018 11:03 AM */ @Component public interface MainActivityComponent { void inject(MainActivity mainActivity); }
Build之后apt會(huì)自動(dòng)生成如下的代碼
build后生成.png
我們利用其中的 DaggerMainActivityComponent 進(jìn)行 MainActivity 與 AAA類(lèi) 之間的橋梁搭建。如下代碼
/** * @author chaochaowu * @Description : MainActivity * @class : MainActivity * @time Create at 6/12/2018 10:58 AM */ public class MainActivity extends AppCompatActivity { @Inject AAA aaa; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); DaggerMainActivityComponent.create().inject(this); aaa.get(); } }
至此,我們便完成了構(gòu)造函數(shù)無(wú)參的對(duì)象的依賴(lài)注入。
構(gòu)造函數(shù)含參的對(duì)象注入
相信很多的時(shí)候,我們要用的對(duì)象在構(gòu)造的時(shí)候是需要參數(shù)的,那我們?nèi)绾芜M(jìn)行它們的依賴(lài)注入呢,這里我用 MVP 中的 Presenter 來(lái)進(jìn)行演示。
/** * @author chaochaowu * @Description : MainActivity * @class : MainActivity * @time Create at 6/12/2018 10:58 AM */ public class MainActivity extends AppCompatActivity implements MainContract.View{ MainContract.Presenter mPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mPresenter = new MainPresenter(this); mPresenter.getData(); } }
上面的代碼是沒(méi)用 Dagger2 時(shí)的 Activity,在 MVP 框架中,由于 Presenter 需要與 View 進(jìn)行交互,需要持有View的對(duì)象,因此在初始化的時(shí)候,需要在構(gòu)造函數(shù)里傳入View對(duì)象作為參數(shù),可以看到代碼中 mPresenter = new MainPresenter(this);
接下來(lái)我們對(duì)它進(jìn)行依賴(lài)注入。首先看一下 Presenter,,Presenter不需要作出任何改變,我們可以看到它的構(gòu)造函數(shù)帶有一個(gè)參數(shù)View
/** * @author chaochaowu * @Description : MainPresenter * @class : MainPresenter * @time Create at 6/11/2018 2:22 PM */ public class MainPresenter implements MainContract.Presenter{ MainContract.View mView; public MainPresenter(MainContract.View mView) { this.mView = mView; } @Override public void getData() { } }
然后我們需要?jiǎng)?chuàng)建一個(gè) MainActivityModule 類(lèi),并在該類(lèi)名加上@Module注解,它含有一個(gè)成員變量 mView 需要在構(gòu)造函數(shù)時(shí)賦值,我們之后會(huì)將這個(gè)變量給 Presenter 進(jìn)行 Presenter 的構(gòu)造,我們還需要寫(xiě)一個(gè)方法,并在其上添加@Provides注解,方法的名字其實(shí)沒(méi)所謂可以隨便取,但是為了代碼的可讀性,一般都以provide開(kāi)頭加上provide的東西的名字,我們?cè)谶@個(gè)方法里返回我們所要提供依賴(lài)的對(duì)象,這里返回了一個(gè) Presenter 對(duì)象,可以看到它在這里的構(gòu)造,我們傳入了參數(shù) View。到這便完成了MainActivityModule 的定義,其實(shí)這個(gè) MainActivityModule 作用就和之前的 構(gòu)造函數(shù)無(wú)參中的 對(duì)無(wú)參構(gòu)造函數(shù) Inject 步驟的性質(zhì)相同。
/** * @author chaochaowu * @Description : MainActivityModule * @class : MainActivityModule * @time Create at 6/11/2018 2:41 PM */ @Module public class MainActivityModule { MainContract.View mView; public MainActivityModule(MainContract.View mView) { this.mView = mView; } @Provides public MainContract.Presenter providePresenter(){ return new MainPresenter(mView); } }
接下來(lái)和 構(gòu)造函數(shù)無(wú)參的對(duì)象注入 中一樣,我們需要定義一個(gè) MainActivityComponent ,并用 @Component 進(jìn)行注解,不一樣的是需要在括號(hào)里加上參數(shù) modules = {MainActivityModule.class} 如下代碼。
/** * @author chaochaowu * @Description : MainActivityComponent * @class : MainActivityComponent * @time Create at 6/11/2018 2:45 PM */ @Component(modules = {MainActivityModule.class}) public interface MainActivityComponent { void inject(MainActivity mainActivity); }
Build一下項(xiàng)目
build之后生成.png
這樣我們就可以像上面一樣,在Activity中調(diào)用DaggerMainActivityComponent 進(jìn)行依賴(lài)注入??匆幌翧ctivity中的代碼。
/** * @author chaochaowu * @Description : MainActivity * @class : MainActivity * @time Create at 6/12/2018 10:58 AM */ public class MainActivity extends AppCompatActivity implements MainContract.View{ @Inject MainContract.Presenter mPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainActivityComponent.builder() .mainActivityModule(new MainActivityModule(this)) .build() .inject(this); mPresenter.getData(); } }
可以看到這里 DaggerMainActivityComponent 調(diào)用的方法有所不同,其中調(diào)用了一個(gè)mainActivityModule() 方法,傳入了一個(gè) MainActivityModule 對(duì)象,該對(duì)象的初始化,我們又傳入了一個(gè)this,也就是View,這個(gè) View就是我們?cè)谥髣?chuàng)建 Presenter 時(shí)傳入的參數(shù) 。
至此,我們就完成了構(gòu)造函數(shù)有參的對(duì)象的依賴(lài)注入。其他有參的、第三方庫(kù)的依賴(lài)注入也是通過(guò)這樣的方式進(jìn)行。
總結(jié)
Dagger2的使用,讓我們代碼的耦合度進(jìn)一步降低了,這是一個(gè)優(yōu)點(diǎn),但是另一方面,使用了Dagger2你需要額外的去定義一些類(lèi),導(dǎo)致代碼的數(shù)量也增加。個(gè)人覺(jué)得,在比較小的項(xiàng)目中,如果去使用,有點(diǎn)emmmm....多此一舉?(劃掉),但是如果一個(gè)項(xiàng)目比較大,代碼本身也比較多,那么使用Dagger2所帶來(lái)的優(yōu)點(diǎn),便可以顯現(xiàn)了。所以說(shuō),是否在項(xiàng)目中使用 Dagger2 仁者見(jiàn)仁智者見(jiàn)智,不過(guò)作為一個(gè)學(xué)習(xí)的點(diǎn),還是挺不錯(cuò)的,嗯。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android開(kāi)源項(xiàng)目PullToRefresh下拉刷新功能詳解
這篇文章主要為大家詳細(xì)介紹了Android開(kāi)源項(xiàng)目PullToRefresh下拉刷新功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09基于Android實(shí)現(xiàn)計(jì)算器計(jì)算功能
這篇文章主要介紹了基于Android實(shí)現(xiàn)計(jì)算器計(jì)算功能,文章通過(guò)代碼示例給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-06-06Android賬號(hào)注冊(cè)實(shí)現(xiàn)點(diǎn)擊獲取驗(yàn)證碼倒計(jì)時(shí)效果
這篇文章主要為大家詳細(xì)介紹了Android賬號(hào)注冊(cè)過(guò)程中實(shí)現(xiàn)點(diǎn)擊獲取驗(yàn)證碼倒計(jì)時(shí)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05基于SurfaceView實(shí)現(xiàn)可拖動(dòng)視頻控件
這篇文章主要為大家詳細(xì)介紹了基于SurfaceView的可拖動(dòng)視頻控件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04Android開(kāi)發(fā)Jetpack組件ViewModel與LiveData使用講解
Jetpack是一個(gè)由多個(gè)技術(shù)庫(kù)組成的套件,可幫助開(kāi)發(fā)者遵循最佳做法,減少樣板代碼并編寫(xiě)可在各種Android版本和設(shè)備中一致運(yùn)行的代碼,讓開(kāi)發(fā)者精力集中編寫(xiě)重要的代碼2022-09-09Android使用AutoCompleteTextView實(shí)現(xiàn)自動(dòng)填充功能的案例
今天小編就為大家分享一篇關(guān)于Android使用AutoCompleteTextView實(shí)現(xiàn)自動(dòng)填充功能的案例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03詳解Android中Service服務(wù)的基礎(chǔ)知識(shí)及編寫(xiě)方法
這篇文章主要介紹了詳解Android中Service服務(wù)的基礎(chǔ)知識(shí)及編寫(xiě)方法,包括Service的啟動(dòng)流程及生命周期等基本內(nèi)容,需要的朋友可以參考下2016-04-04Android編程實(shí)現(xiàn)使用Intent傳輸包含自定義類(lèi)的ArrayList示例
這篇文章主要介紹了Android編程實(shí)現(xiàn)使用Intent傳輸包含自定義類(lèi)的ArrayList,涉及Android對(duì)象序列化、反序列化、Intent數(shù)據(jù)傳輸?shù)认嚓P(guān)操作技巧,需要的朋友可以參考下2017-08-08Android開(kāi)發(fā)之圖片壓縮實(shí)現(xiàn)方法分析
這篇文章主要介紹了Android開(kāi)發(fā)之圖片壓縮實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了Android圖片壓縮的原理、實(shí)現(xiàn)方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-03-03Android仿微信QQ聊天頂起輸入法不頂起標(biāo)題欄的問(wèn)題
這篇文章主要介紹了Android之仿微信QQ聊天頂起輸入法不頂起標(biāo)題欄問(wèn)題,本文實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-11-11