Dagger2學習筆記(一)

系列文章:
Dagger2學習筆記(一)
Dagger2學習筆記(二)

依賴注入是一種十分好的技巧,它能解偶高層次模塊和低層次模塊当叭,使得高層模塊不用將底層模塊硬編碼到內(nèi)部盖灸。
所有依賴的底層模塊都由外部注入,實際是一種面向接口編程醉箕。高層模塊不依賴底層模塊的實現(xiàn)細節(jié)徙垫,可以方便的做到替換底層模塊。
這種技術(shù)在編寫跨平臺程序的時候可以很容易的替換調(diào)依賴系統(tǒng)的底層模塊己英,并且在做單元測試的時候也可以很容易的使用stub對象注入宿主類中從而方便宿主類的測試代碼的編寫逗抑。

使用Dagger2實現(xiàn)依賴注入

如果不使用DI框架,我們也可以在構(gòu)造方法里傳入依賴類或著用setter方法來將依賴類注入宿主類荧关。但是這樣的話就會需要我們在業(yè)務邏輯中處理依賴類的生成和注入褂傀,其實這些依賴的注入代碼和業(yè)務都沒有什么關(guān)系,僅僅是一些初始化的操作而已同波,如果可以將這些與業(yè)務邏輯無關(guān)的代碼都獨立出去,這樣的話我們的代碼邏輯就會更加的簡潔和清晰未檩。Dagger2就是一個十分強大的DI框架冤狡,它可以幫助我們輕松的在業(yè)務邏輯之外實現(xiàn)依賴注入。

下面我將用一個小Demo來介紹一下Dagger2的用法悲雳。這個小Demo的功能是通過github帳號搜索用戶頭像和用戶名合瓢,同時列出該用戶的follower

Dagger2的引入

Dagger2沒有使用反射,它是通過編譯時生成代碼來實現(xiàn)依賴注入的晴楔。所以需要引入apt:

//build.gradle(project)
...
buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.0'
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}
...
//build.gradle(app)
apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
...

之后再引入javax.annotation和dagger2:

//build.gradle(app)
...
dependencies {
    ...
    compile 'com.google.dagger:dagger:2.4'
    apt 'com.google.dagger:dagger-compiler:2.4'
    compile 'org.glassfish:javax.annotation:10.0-b28'
    ...
}

Dagger2的兩個重要組件

Dagger2有兩個十分重要的組件:Module和Component滥崩。

  • Module

    Module是依賴的提供者,Dagger2框架通過Module的Provides方法獲取被依賴類的實例。

  • Component

    Component是一個注入接口短条,Dagger2框架通過Component將依賴注入到高層類中才菠。

    用一個形象的比喻來說明就是Module是裝有被依賴類的針筒,Component是針頭可都。Dagger2通過選擇針筒和針頭的不同組合可以將不同的被依賴實例注入到高層模塊中蚓耽。

實現(xiàn)搜索頁面

@Inject注解

我們的搜索頁面很簡單,只有一個輸入框和一個搜索安按鈕签杈,它的作用是輸入要搜索的用戶的賬號鼎兽。我們使用MVP模式去實現(xiàn)它,因為它不需要model層铣除,所以只有View和Presenter:

public interface SearchView {
    ...
}
public class SearchPresenter{
    ...
    @Inject
    SearchView mSearchView;

    @Inject
    Context mContext;

    @Inject
    public SearchPresenter() {
        Log.d(TAG, "SearchPresenter()");
    }
    ...
}
public class SearchActivity extends Activity implements SearchView {
    ...
    @Inject
    SearchPresenter mSearchPresenter;
    ...
}

我們通過@Inject注解告訴Dagger2哪些成員變量是需要被注入的尚粘,這里需要注意的是被@Inject標注的成員變量不可以是private的长已,因為Dagger2沒有用到反射,而是通過生成代碼去完成注入的潘明,所以一旦你將成員變量聲明成private的,那Dagger2就不能訪問到它厚宰,從而無法無法完成注入了遂填。@Inject還有另外一個作用就是告訴Dagger2用哪個構(gòu)造函數(shù)去創(chuàng)建實例,如這里Dagger2就會用SearchPresenter()去創(chuàng)建SearchPresenter的實例撵幽,這個構(gòu)造函數(shù)的作用在接下來就會被講到礁击。

Module

然后再讓我們來看看SearchPresenterModule:

@Module
public class SearchPresenterModule {
    private SearchActivity mSearchActivity;

    public SearchPresenterModule(SearchActivity view) {
        mSearchActivity = view;
    }

    @Provides
    SearchView provideSearchView() {
        return mSearchActivity;
    }

    @Provides
    Context provideContext() {
        return mSearchActivity;
    }
}

注入SearchPresenter所需要的SearchView和Context就是從這里提供的

Module類首先需要使用@Module注解標注哆窿,讓Dagger2知道這是一個Module,然后內(nèi)部的使用@Provides注解標注的方法就是用來獲取被依賴類的實例的方法,例如provideSearchView就可以用來提供SearchView

一般我習慣@Provide方法加上provide前綴强衡,但是這個也不是必須码荔,可以沒有這個前綴。

Component

接著看看Component:

@Component(modules = {SearchPresenterModule.class})
public interface SearchComponent {
    void inject(SearchActivity activity);

    void inject(SearchPresenter presenter);
}

Component是一個被@Component注解標注的接口越败,Dagger2會自動生成實現(xiàn)這個接口的類誉己,去完成注入的功能。我們需要用modules去告訴Component從哪個Module中獲取被依賴類的實例噪猾。這里Dagger2就會自動生成實現(xiàn)了SearchComponent接口的DaggerSearchComponent類,它有兩個方法袱蜡,分別用來向SearchActivity和SearchPresenter注入依賴坪蚁。

向SearchPresenter注入的SearchView和Context都是SearchPresenterModule提供的這個很容易理解,但是向SearchActivity注入的SearchPresenter又是從哪里來的呢?還記得我們用@Inject標注了SearchPresenter的一個構(gòu)造函數(shù)了嗎贱田?Dagger2會使用我們標注的構(gòu)造函數(shù)創(chuàng)建出一個SearchPresenter來給SearchActivity注入使用嘴脾。

調(diào)用注入方法實現(xiàn)注入

在SearchActivity的onCreate方法中將依賴注入到SearchActivity和SearchPresenter中:

SearchComponent component = DaggerSearchComponent.builder()
                .searchPresenterModule(new SearchPresenterModule(this))
                .build();

component.inject(this);
component.inject(mSearchPresenter);

它實際是通過查找SearchActivity和SearchPresenter中帶有@Inject注解的成員變量知道哪個變量需要被注入,然后通過SearchPresenterModule的provide方法和SearchPresenter被標注的構(gòu)造方法獲取到被依賴類的實例去實現(xiàn)注入的耗拓。

這里有一點需要注意的是調(diào)用順序奏司,inject(SearchActivity activity)要在inject(SearchPresenter presenter)前面調(diào)用,因為需要先將SearchActivity.this的mSearchPresenter注入竿刁,才能向mSearchPresenter中再注入SearchActivity

指定構(gòu)造函數(shù)

我們在前面講到過@Inject可以指定構(gòu)造函數(shù)搪缨,其實它還有另一重意義,就是存在多個構(gòu)造函數(shù)的時候選擇其中一種。

我們現(xiàn)在添加另外一種SearchPresenter構(gòu)造函數(shù),然后中添加打印:

public class SearchPresenter{
    ...
    @Inject
    SearchView mSearchView;

    @Inject
    Context mContext;

    @Inject
    public SearchPresenter() {
        Log.d(TAG, "SearchPresenter()");
    }

    public SearchPresenter(Context context) {
        Log.d(TAG, "SearchPresenter(Context context)");
        mContext = context;
    }
    ...
}

讓我們看看運行的時候到底調(diào)的是哪個構(gòu)造函數(shù)吧:

D/SearchPresenter(27333): SearchPresenter()

如果我們把SearchPresenter類修改一下呢?

public class SearchPresenter{
    ...
    @Inject
    SearchView mSearchView;

    // @Inject 注釋掉
    Context mContext;

    // @Inject 注釋掉
    public SearchPresenter() {
        Log.d(TAG, "SearchPresenter()");
    }

    @Inject // 添加@Inject
    public SearchPresenter(Context context) {
        Log.d(TAG, "SearchPresenter(Context context)");
        mContext = context;
    }
    ...
}

現(xiàn)在可以看到打印:

D/SearchPresenter(27693): SearchPresenter(Context context)

從打印來看,@Inject的確是可以選擇構(gòu)造函數(shù)的煮盼。但還有個細節(jié)不知道大家有沒有注意到,我們?nèi)サ袅薽Context的@Inject,改由構(gòu)造函數(shù)傳入。這個傳入構(gòu)造函數(shù)的Context又是怎么來的呢香到?

答案在SearchPresenterModule里:

@Module
public class SearchPresenterModule {
    private SearchActivity mSearchActivity;

    public SearchPresenterModule(SearchActivity view) {
        mSearchActivity = view;
    }

    @Provides
    SearchView provideSearchView() {
        return mSearchActivity;
    }

    // 是它,是它,就是它
    @Provides
    Context provideContext() {
        return mSearchActivity;
    }
}

沒錯SearchPresenterModule.provideContext()這個方法還能創(chuàng)建Context出來給SearchPresenter的構(gòu)造函數(shù)使用悠就!

Demo地址

可以在這里查看完整代碼,剩余部分的代碼會在下一篇文章里介紹梗脾。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末炸茧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子辕狰,更是在濱河造成了極大的恐慌控漠,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柬脸,死亡現(xiàn)場離奇詭異倒堕,居然都是意外死亡爆价,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門骤宣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來序愚,“玉大人,你說我怎么就攤上這事芬膝⌒谓浚” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵癣缅,是天一觀的道長。 經(jīng)常有香客問我祷膳,道長爬立,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任抡秆,我火速辦了婚禮吟策,結(jié)果婚禮上檩坚,老公的妹妹穿的比我還像新娘。我一直安慰自己匾委,他們只是感情好赂乐,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著挖滤,像睡著了一般浅役。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上惧盹,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天瞪讼,我揣著相機與錄音,去河邊找鬼。 笑死背亥,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的狡汉。 我是一名探鬼主播盾戴,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼橄仆!你這毒婦竟也來了衅斩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤您宪,失蹤者是張志新(化名)和其女友劉穎宪巨,沒想到半個月后溜畅,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡天吓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年龄寞,在試婚紗的時候發(fā)現(xiàn)自己被綠了汤功。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡色解,死狀恐怖餐茵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情锣笨,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布入撒,位于F島的核電站椭岩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏献雅。R本人自食惡果不足惜姨伟,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一夺荒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧技扼,春花似錦、人聲如沸窍箍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽邪狞。三九已至茅撞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間剑令,已是汗流浹背拄查。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留腺毫,地道東北人。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像急黎,于是被迫代替她去往敵國和親侧到。 傳聞我的和親對象是個殘疾皇子勃教,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

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