Dagger2的輕松愉悅解析

  Dagger2,依賴注入框架,一個(gè)剛接觸時(shí)感覺麻煩,用久了就會(huì)“嘴上說不要,身體卻很誠(chéng)實(shí)”的開發(fā)潤(rùn)滑劑(???)。(本文為拖更而生)

一、Dagger2 介紹

1、為什么使用dagger2

?誰用誰知道Σ( ̄。 ̄?)?,如絲般順滑,奶不死的Dagger2 ,主要優(yōu)勢(shì)體現(xiàn)在:

  • 解決項(xiàng)目中多實(shí)例依賴創(chuàng)建問題,如:new A(new B(new C()))
  • 更好的對(duì)象生命周期依賴和管理,如通過@Scope規(guī)范實(shí)例的生命周期。
  • 規(guī)范代碼,提高解耦能力,增強(qiáng)代碼的拓展能力,如類的依賴、創(chuàng)建、復(fù)用、拓展都通過@Component@Module@Inject的規(guī)范實(shí)現(xiàn)。
  • 最重要的是:代碼看起來比較裝逼!
困了嗎?困了我們就開始咯

2、簡(jiǎn)單原理介紹

?Dagger2 可以理解為一套開發(fā)規(guī)范,遵守這套規(guī)范編寫的代碼,通過Dagger2 的運(yùn)行時(shí)注解,在編譯時(shí)自動(dòng)生成模版代碼,已達(dá)到注入和復(fù)用的目的。

?自動(dòng)生成的代碼,一般存在路徑build\generated\source\apt下。那么了解完這套模版規(guī)范,Dagger2 將不再神秘,“深入淺出”“指日可待”(? ̄? ??  ̄??)啊

關(guān)于運(yùn)行時(shí)注解不了解的可查閱:《Android注解快速入門和實(shí)用解析》

二、Dagger2 剖析

?讓我們循環(huán)漸進(jìn)的開始吧。

?首先看下圖,Dagger2中主要的三個(gè)注解是 :@Inject@Component@Module

?它們是最基礎(chǔ),也是使用最多注解,我們將從它們身上開始“摸索”Σ( ̄。 ̄?)?。

  • @Inject 指向需要構(gòu)成注入的類和環(huán)境。
  • @Module 提供生成對(duì)象所需的參數(shù)。(一般是在 @Inject 注解的對(duì)象,其構(gòu)造函數(shù)無法添加 @Inject時(shí)使用。
  • @Component 作為中間橋梁連接注入對(duì)象和工廠。
主要關(guān)系圖

1、Inject

?一切的“插入”從它開始!@Inject指定需要注入的類和環(huán)境,如下方圖2,TasksActivity 中 mTasksPresenter 是被Inject注解的對(duì)象,同時(shí)TasksPresenter 的構(gòu)造函數(shù)也被Inject注解。

?內(nèi)部成員被 @Inject 注解時(shí),如 mTasksPresenter,Dagger2 就會(huì)找 TasksPresenter 中被 @Inject 注解的構(gòu)造函數(shù),如果找到了,便對(duì)其構(gòu)建注入。

?那么問題來了!這時(shí)候如果 TasksPresenter 的構(gòu)造方法為空構(gòu)造方法,便萬事大吉。可惜現(xiàn)實(shí)總是那么騷!圖中 TasksPresenter 的構(gòu)造函數(shù)有參,需要 tasksRepositorytasksView 兩個(gè)參數(shù),那便是后面的@Module的用武之地,它將提供“后門注入”支持。

我是圖2

?既然知道 @Module 的作用,我們先繼續(xù)往下走,構(gòu)造方法被注解的類,會(huì)生成一個(gè)繼承Factory的類,如 TasksPresenter 生成 TasksPresenter_Factory , 如下圖3,這是由Dagger2的自動(dòng)編譯生成的,這個(gè)工廠用于提供實(shí)例化類,其中的get()方法便是在注入時(shí)被調(diào)用。

?當(dāng)然,你完全可以不關(guān)心這些。但是了解這些,有時(shí)候可以更愉悅的裝逼(-_^),所以我選擇憋著往下看

圖三圖三

?繼續(xù)深入,如下圖四,在被 @Inject 注解的內(nèi)部參數(shù)或方法,會(huì)生成對(duì)應(yīng)的繼承MembersInjector的類, 如 TasksActivity_MembersInjector。在它調(diào)用.injectMembers(this);時(shí),實(shí)際上就是調(diào)用了上面 TasksPresenter_Factoryget() 方法注入進(jìn)去的。

?看到?jīng)],這不就聯(lián)系起來了么,城市套路深啊,而且還很滑!(? ̄? ??  ̄??)

圖四賽高

2、Module

?上面說過,因?yàn)闃?gòu)造方法包含參數(shù),而所包含的參數(shù),其構(gòu)造方法無法被 @Inject 注解,這時(shí)候就需要 @Module 提供“后門”。

?舉個(gè)栗子:類A的構(gòu)造方法需要類B,但類B的構(gòu)造方法無法添加 @Inject 注解。這時(shí)候通過 @Module ,在內(nèi)部使用 @Provides注解的以provide開頭的方法,這些方法就是所提供注入所需的依賴,如圖5

如圖5

?同樣是自動(dòng)生成模版代碼,@Module 注解的類中,每一個(gè) @Provides修飾的方法,都會(huì)生成一個(gè)工廠類,如圖六下生成:TasksPresenterModule_ProvideTasksContractViewFactory ,通過get()方法提供注入時(shí)所需的參數(shù)。

?這時(shí)候回到圖三,可以看到這些get()方法,便是在 TasksPresenter_Factoryget() 被調(diào)用時(shí)使用 看到?jīng)],這不又聯(lián)系起來了么,套路套路啊!(? ̄? ??  ̄??)

圖六六六六六

3、Component

?做了那么多,總得用起來吧。Component 就是將 @Inject@Module 聯(lián)系起來的橋梁,從 @Module 中獲取依賴,并將依賴注入給 @Inject 注解的參數(shù)。

?如圖七@Component 接口指定了使用的 Module 和依賴的 Component。是的,Component依賴,這樣更有利于代碼的復(fù)用。

圖七

@Component的接口的內(nèi)部方法簡(jiǎn)單可分為:

  • 如果是void就必須有注入環(huán)境類。
  • 如果參數(shù)為空,就必須有返回類。返回類必須有 @Inject 提供構(gòu)造方法類,或者引用的 @Module 有 @Provide提供的。

?同樣的套路,TaskComponent會(huì)生成 DaggerTaskComponent 類,這個(gè)類便是我們需要使用的對(duì)象,如圖八圖九,可以看到上面生成的對(duì)象,都是在其中被使用,最后通過我們定義好的 inject() 方法,如下方代碼實(shí)現(xiàn)注入效果。

//注入咯注入咯
DaggerTasksComponent.builder()
        .tasksRepositoryComponent(((ToDoApplication) getApplication()).getTasksRepositoryComponent())
        .tasksPresenterModule(new TasksPresenterModule(tasksFragment)).build()
        .inject(this);
圖爸

圖舅

?怎么樣,這樣的套路清晰了么?(? ̄? ??  ̄??),簡(jiǎn)單總結(jié)起來其實(shí)如下方這樣,需要我們動(dòng)手的,其實(shí)并不多:

  • 使用 @Inject 注解需要注入的參數(shù)和方法。
  • 使用 @Module 提供構(gòu)造方法中無法注解的參數(shù)。
  • 使用 @Component 連起起來,并且調(diào)用注入。

4、關(guān)聯(lián)總結(jié)

  來簡(jiǎn)單總結(jié)下生成模版的關(guān)系,不感興趣的可以略過···吧(#?Д?):

  • 注解構(gòu)造方法,生成的 TasksPresenter_Factory ,內(nèi)部通過 create 方法 創(chuàng)建Factory,通過get方法 提供 TasksPresenter 對(duì)象。

  • TasksModule_ProvideTasksViewFactory ,由Module中@Provide注解的方法生成,內(nèi)部通過 create 方法創(chuàng)建,通過 get 方法提供 TasksPresenter_Factory 在創(chuàng)建 TasksPresenter 需要的參數(shù)。

  • TasksComponent生成DaggerTasksComponent 初始化時(shí) ,通過 TasksModule_ProvideTasksViewFactory作為參數(shù)創(chuàng)建了 TasksPresenter_Factory , 在TasksActivity_MembersInjector 中,通過injectMembers 方法,利用 TasksPresenter_Factoryget 實(shí)現(xiàn)注入。

  • DaggerTasksComponent 中外部提供Builder設(shè)置方法,和依賴的Component與module數(shù)量有關(guān)。如果依賴的未被使用,會(huì)有@deprecated提示。

總結(jié)圖

三、稍微再“深入”

1、Scope

?讓我們?cè)偕晕⑸钊胍稽c(diǎn)去了解Dagger2吧,生命周期是值得關(guān)心的。比如內(nèi)置的 @Singleton 注解,字面上就是單例的意思,但是實(shí)際上直接使用 @Singleton 并不會(huì)有單例的效果。

圖十@Singleton注解的class

?@Singleton實(shí)際上是一種規(guī)范注解,它屬于 @Scope 注解的一種,正如字面上所示,它代表著單例的生命周期,事實(shí)上 @Scope 是一種作用域注解,通過Component、Module一其配種使用,才能達(dá)到作用域的效果。

?如上圖十下圖11,12中, TasksRepository、TasksRepositoryComponent 、TasksRepositoryModule 都被 @Singleton 注解了,但是這并不代表著他們就是單例,只是通過注解限定作用域,并且在字面上表明了它們單例。

圖11
圖12

?真正的單例效果, 是下圖13中在 ToDoApplicationDaggerTasksRepositoryComponent 的創(chuàng)建。粗俗點(diǎn)說,就是因?yàn)锳pplication中的 mRepositoryComponent 對(duì)象只有一個(gè),由于作用域 @Scope 的作用,那么getTasksRepository() 獲取的TasksRepository 達(dá)到了單例的效果。

圖13

?這里理解下來,如下方代碼,我們創(chuàng)建一個(gè)@ActivityScoped 的Scope注解,限定注入對(duì)象 TasksPresenter 的生命周期是和Activity相關(guān)的,然后我們?cè)?TasksActivity 中,調(diào)用 DaggerTasksComponent 注入。因?yàn)?DaggerTasksComponentTasksActivity 中唯一,所以 TasksPresenter 在Activity中也唯一。

@Documented
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScoped {
}
········
@ ActivityScoped
public class TasksRepository  implements TasksDataSource{
  ···
}
·······
public class TasksActivity extends AppCompatActivity {

    @Inject TasksPresenter mTasksPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.tasks_act);

        // Create the presenter
        DaggerTasksComponent.builder()
                .tasksRepositoryComponent(((ToDoApplication) getApplication()).getTasksRepositoryComponent())
                .tasksPresenterModule(new TasksPresenterModule(tasksFragment)).build()
                .inject(this);

    }

?上面這段看起來是不是很多余?有點(diǎn)廢話?

?但是確也是最好理解的,如果換個(gè)方法,比如把 @Inject TasksPresenter mTasksPresenter; 寫在Fragment,DaggerTasksComponent 在Activity構(gòu)建,然后在多個(gè)Fragment的onCreateView調(diào)用inject(this)呢?

?很明顯注入的mTasksPresenter對(duì)象就是Activity生命周期,而和Fragment生命周期無關(guān),并且由于Dagger2內(nèi)部的限定,Scope可以更好的規(guī)范Module和Component的生命周期。

?還有更多的場(chǎng)景和規(guī)范要求,這里就不一一展開了,有興趣深入了解的可谷歌之。(肯定不是因?yàn)閼械谜归_!!)

倦了倦了

2、Qualifier

?簡(jiǎn)單的理解,它就是一個(gè)Tag標(biāo)記的作用。

?如下方代碼,我們定義了Remote和Local兩種@Qualifier 注解,在Module中,提供的 TasksDataSource通過Remote和Local兩種Tag區(qū)分,這樣在注入的時(shí)候,也可以通過Tag指定注入?yún)?shù)。

?很好理解吧!這里就不展開了,一展開了又是一堆····╮(╯▽╰)╭,是時(shí)候收尾了,你(wo)也需要休息休息了。

//類型一
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Remote {
}
······
//類型二
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Local {
}
······
//提供時(shí)區(qū)分類型
@Module
abstract class TasksRepositoryModule {

    @Singleton
    @Local
    abstract TasksDataSource provideTasksLocalDataSource(TasksLocalDataSource dataSource);

    @Singleton
    @Remote
    abstract TasksDataSource provideTasksRemoteDataSource(FakeTasksRemoteDataSource dataSource);
}
······
//根據(jù)類型注入
 @Inject
 TasksRepository(@Remote TasksDataSource tasksRemoteDataSource,
            @Local TasksDataSource tasksLocalDataSource) {
        mTasksRemoteDataSource = tasksRemoteDataSource;
        mTasksLocalDataSource = tasksLocalDataSource;
 }
最后推薦下相關(guān)的demo
  • android-architecture : todo-mvp-dagger2 分支中有詳細(xì)的demo演示。

  • LazyRecyclerAdapter :個(gè)人在這個(gè)開源項(xiàng)目包含有Dagger2在java和kotlin中的使用demo。

熟悉的口味
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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