Dagger2的輕鬆愉悅解析

  Dagger2,依賴注入框架,一個剛接觸時感受麻煩,用久了就會「嘴上說不要,身體卻很誠實」的開發潤滑劑(◐‿◑)。(本文爲拖更而生)java

1、Dagger2 介紹

一、爲何使用dagger2

 誰用誰知道Σ( ̄。 ̄ノ)ノ,如絲般順滑,奶不死的Dagger2 ,主要優點體如今:android

  • 解決項目中多實例依賴建立問題,如:new A(new B(new C()))
  • 更好的對象生命週期依賴和管理,如經過@Scope規範實例的生命週期。
  • 規範代碼,提升解耦能力,加強代碼的拓展能力,如類的依賴、建立、複用、拓展都經過@Component@Module@Inject的規範實現。
  • 最重要的是:代碼看起來比較裝逼!

困了嗎?困了咱們就開始咯
困了嗎?困了咱們就開始咯

二、簡單原理介紹

 Dagger2 能夠理解爲一套開發規範,遵照這套規範編寫的代碼,經過Dagger2 的運行時註解,在編譯時自動生成模版代碼,已達到注入和複用的目的。git

 自動生成的代碼,通常存在路徑build\generated\source\apt下。那麼瞭解完這套模版規範,Dagger2 將再也不神祕,「深刻淺出」「指日可待」(˶‾᷄ ⁻̫ ‾᷅˵)啊github

關於運行時註解不瞭解的可查閱:《Android註解快速入門和實用解析》bash

2、Dagger2 剖析

讓咱們循環漸進的開始吧。框架

 首先看下圖,Dagger2中主要的三個註解是 :@Inject@Component@Moduleide

 它們是最基礎,也是使用最多註解,咱們將從它們身上開始「摸索」Σ( ̄。 ̄ノ)ノ。函數

  • @Inject 指向須要構成注入的類和環境。
  • @Module 提供生成對象所需的參數。(通常是在 @Inject 註解的對象,其構造函數沒法添加 @Inject時使用。
  • @Component 做爲中間橋樑鏈接注入對象和工廠。

主要關係圖
主要關係圖

一、Inject

 一切的「插入」從它開始!@Inject指定須要注入的類和環境,以下方圖2,TasksActivity 中 mTasksPresenter 是被Inject註解的對象,同時TasksPresenter 的構造函數也被Inject註解。ui

 內部成員被 @Inject 註解時,如 mTasksPresenter,Dagger2 就會找 TasksPresenter 中被 @Inject 註解的構造函數,若是找到了,便對其構建注入。this

 那麼問題來了!這時候若是 TasksPresenter 的構造方法爲空構造方法,便萬事大吉。惋惜現實老是那麼騷!圖中 TasksPresenter 的構造函數有參,須要 tasksRepositorytasksView 兩個參數,那即是後面的@Module的用武之地,它將提供「後門注入」支持。

我是圖2
我是圖2

 既然知道 @Module 的做用,咱們先繼續往下走,構造方法被註解的類,會生成一個繼承Factory的類,如 TasksPresenter 生成 TasksPresenter_Factory , 以下圖3,這是由Dagger2的自動編譯生成的,這個工廠用於提供實例化類,其中的get()方法即是在注入時被調用。

固然,你徹底能夠不關心這些。可是瞭解這些,有時候能夠更愉悅的裝逼(-_^),因此我選擇憋着往下看

圖三圖三
圖三圖三

 繼續深刻,以下圖四,在被 @Inject 註解的內部參數或方法,會生成對應的繼承MembersInjector的類, 如 TasksActivity_MembersInjector。在它調用.inject(this);時,實際上就是調用了上面 TasksPresenter_Factoryget() 方法注入進去的。

 看到沒,這不就聯繫起來了麼,城市套路深啊,並且還很滑!(˶‾᷄ ⁻̫ ‾᷅˵)

圖四賽高
圖四賽高

二、Module

 上面說過,由於構造方法包含參數,而所包含的參數,其構造方法沒法被 @Inject 註解,這時候就須要 @Module 提供「後門」。

 舉個栗子:類A的構造方法須要類B,但類B的構造方法沒法添加 @Inject 註解。這時候經過 @Module ,在內部使用 @Provides註解的以provide開頭的方法,這些方法就是所提供注入所需的依賴,如圖5

如圖5
如圖5

 一樣是自動生成模版代碼,@Module 註解的類中,每個 @Provides修飾的方法,都會生成一個工廠類,如圖六下生成:TasksModule_ProvideTasksViewFactory ,經過get()方法提供注入時所需的參數。

 這時候回到圖四,能夠看到這些get()方法,即是在 TasksPresenter_Factoryget() 被調用時使用 看到沒,這不又聯繫起來了麼,套路套路啊!(˶‾᷄ ⁻̫ ‾᷅˵)

圖六六六六六
圖六六六六六

三、Component

 作了那麼多,總得用起來吧。Component 就是將 @Inject@Module 聯繫起來的橋樑,從 @Module 中獲取依賴,並將依賴注入給 @Inject 註解的參數。

 如圖七@Component 接口指定了使用的 Module 和依賴的 Component。是的,Component依賴,這樣更有利於代碼的複用。

  
  

圖七
圖七

@Component的接口的內部方法簡單可分爲:

  • 若是是void就必須有注入環境類。
  • 若是參數爲空,就必須有返回類。返回類必須有 @Inject 提供構造方法類,或者引用的 @Module 有 @Provide提供的。

 一樣的套路,TaskComponent會生成 DaggerTaskComponent 類,這個類即是咱們須要使用的對象,如圖八圖九,能夠看到上面生成的對象,都是在其中被使用,最後經過咱們定義好的 inject() 方法,以下方代碼實現注入效果。

//注入咯注入咯
DaggerTasksComponent.builder()
        .tasksRepositoryComponent(((ToDoApplication) getApplication()).getTasksRepositoryComponent())
        .tasksPresenterModule(new TasksPresenterModule(tasksFragment)).build()
        .inject(this);複製代碼

圖爸
圖爸

圖舅
圖舅

怎麼樣,這樣的套路清晰了麼?(˶‾᷄ ⁻̫ ‾᷅˵),簡單總結起來其實以下方這樣,須要咱們動手的,其實並很少:

  • 使用 @Inject 註解須要注入的參數和方法。
  • 使用 @Module 提供構造方法中沒法註解的參數。
  • 使用 @Component 連起起來,而且調用注入。

四、關聯總結

  來簡單總結下生成模版的關係,不感興趣的能夠略過···吧(#゚Д゚):

  • 註解構造方法,生成的 TasksPresenter_Factory ,內部經過 create 方法 建立Factory,經過get方法 提供 TasksPresenter 對象。

  • TasksModule_ProvideTasksViewFactory ,由Module中@Provide註解的方法生成,內部經過 create 方法建立,經過 get 方法提供 TasksPresenter_Factory 在建立 TasksPresenter 須要的參數。

  • TasksComponent生成DaggerTasksComponent 初始化時 ,經過 TasksModule_ProvideTasksViewFactory做爲參數建立了 TasksPresenter_Factory , 在TasksActivity_MembersInjector 中,經過injectMembers 方法,利用 TasksPresenter_Factoryget 實現注入。

  • DaggerTasksComponent 中外部提供Builder設置方法,和依賴的Component與module數量有關。若是依賴的未被使用,會有@deprecated提示。

總結圖
總結圖

3、稍微再「深刻」

一、Scope

 讓咱們再稍微深刻一點去了解Dagger2吧,生命週期是值得關心的。好比內置的 @Singleton 註解,字面上就是單例的意思,可是實際上直接使用 @Singleton 並不會有單例的效果。

圖十@Singleton註解的class
圖十@Singleton註解的class

@Singleton其實是一種規範註解,它屬於 @Scope 註解的一種,正如字面上所示,它表明着單例的生命週期,事實上 @Scope 是一種做用域註解,經過Component、Module一其配種使用,才能達到做用域的效果。

 如上圖十下圖11,12中, TasksRepository、TasksRepositoryComponent 、TasksRepositoryModule 都被 @Singleton 註解了,可是這並不表明着他們就是單例,只是經過註解限定做用域,而且在字面上代表了它們單例。

圖11
圖11

圖12
圖12

 真正的單例效果, 是下圖13中在 ToDoApplicationDaggerTasksRepositoryComponent 的建立。粗俗點說,就是由於Application中的 mRepositoryComponent 對象只有一個,因爲做用域 @Scope 的做用,那麼getTasksRepository() 獲取的TasksRepository 達到了單例的效果。

圖13
圖13

 這裏理解下來,以下方代碼,咱們建立一個@ActivityScoped 的Scope註解,限定注入對象 TasksPresenter 的生命週期是和Activity相關的,而後咱們在 TasksActivity 中,調用 DaggerTasksComponent 注入。由於 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);

    }複製代碼

 上面這段看起來是否是不少餘?有點廢話?

 可是確也是最好理解的,若是換個方法,好比把 @Inject TasksPresenter mTasksPresenter; 寫在Fragment,DaggerTasksComponent 在Activity構建,而後在多個Fragment的onCreateView調用inject(this)呢?

 很明顯注入的mTasksPresenter對象就是Activity生命週期,而和Fragment生命週期無關,而且因爲Dagger2內部的限定,Scope能夠更好的規範Module和Component的生命週期。

 還有更多的場景和規範要求,這裏就不一一展開了,有興趣深刻了解的可谷歌之。(確定不是由於懶得展開!!)

倦了倦了
倦了倦了

二、Qualifier

 簡單的理解,它就是一個Tag標記的做用。

 以下方代碼,咱們定義了Remote和Local兩種@Qualifier 註解,在Module中,提供的 TasksDataSource經過Remote和Local兩種Tag區分,這樣在注入的時候,也能夠經過Tag指定注入參數。

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

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

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

    @Singleton
    @Remote
    abstract TasksDataSource provideTasksRemoteDataSource(FakeTasksRemoteDataSource dataSource);
}
······
//根據類型注入
 @Inject
 TasksRepository(@Remote TasksDataSource tasksRemoteDataSource,
            @Local TasksDataSource tasksLocalDataSource) {
        mTasksRemoteDataSource = tasksRemoteDataSource;
        mTasksLocalDataSource = tasksLocalDataSource;
 }複製代碼
最後推薦下相關的demo

熟悉的口味
熟悉的口味
相關文章
相關標籤/搜索