Android:dagger2讓你愛不釋手-重點概念講解、融合篇

前言

Android:dagger2讓你愛不釋手-基礎依賴注入框架篇這篇講解了Inject,Component,Module,Provides是如何構成dagger2整個依賴注入框架


component_module_inject.png

因爲dagger2的整個依賴注入框架已經構建完成,所以dagger2中剩下的Qualifier(限定符)、Singleton(單例)、Scope(作用域),SubComponent概念基本都是在對整個依賴注入框架進行細節上的完善。
我還是依然從抽象概念的角度講解,講解每個概念在整個依賴注入框架中到底起了什麼作用,因爲dagger2本身不容易上手,只有真正的瞭解了每個概念的作用,在使用時纔會得心應手,大家別急,後面的章節會有dagger2的sample。

本節內容

  • Qualifier(限定符)、Singleton(單例)、Scope(作用域)、Component的組織方式概念講解
  • dagger2能帶來哪些實惠?
    在講解時,我還依然沿用上一節的講解方式,由簡入難不斷深入的進行。

Qualifier(限定符)是什麼鬼?

上一節已經提到,Component是一個注入器(Injector),同時也起着橋樑的作用,一端是創建類實例端(創建類實例即負責生產類實例,下面會用該詞來指代),另一端是目標類端(目標類需要進行依賴初始化的類,下面都會用目標類一詞來指代),請看下圖:


新的關係.png

創建類實例有2個維度可以創建:

  • 通過用Inject註解標註的構造函數來創建(以下簡稱Inject維度)
  • 通過工廠模式的Module來創建(以下簡稱Module維度)

這2個維度是有優先級之分的,Component會首先從Module維度中查找類實例,若找到就用Module維度創建類實例,並停止查找Inject維度。否則纔是從Inject維度查找類實例。所以創建類實例級別Module維度要高於Inject維度。

現在有個問題,基於同一個維度條件下,若一個類的實例有多種方法可以創建出來,那注入器(Component)應該選擇哪種方法來創建該類的實例呢?如下圖,基於Inject維度:


Qualifier_迷失.png

我把上面遇到的問題起個名字叫依賴注入迷失
那麼可以給不同的創建類實例的方法用標識進行標註,用標識就可以對不同的創建類實例的方法進行區分(標識就如給不同的創建類實例方法起了一個id值)。同時用要使用的創建類實例方法的標識目標類相應的實例屬性進行標註。那這樣我們的問題就解決了,提到的標識就是Qualifier註解,當然這種註解得需要我們自定義。

Qualifier(限定符)就是解決依賴注入迷失問題的。
注意
dagger2在發現依賴注入迷失時在編譯代碼時會報錯。

Scope(作用域)你真是挺坑的一個東東

我們暫且不介紹Singleton,因爲它是Scope的一個默認實現,理解了Scope自然就理解Singleton了。
爲什麼要說Scope比較坑呢,在剛開始接觸Scope的時候,看了網上各種關於Scope的介紹,總結Scope的作用是:

Dagger2可以通過自定義Scope註解,來限定通過Module和Inject方式創建的類的實例的生命週期能夠與目標類的生命週期相同。或者可以這樣理解:通過自定義Scope註解可以更好的管理創建的類實例的生命週期。

網上也有各種例子比如:自定義一個PerActivity註解,那創建的類實例就與Activity“共生死“
或者用Singleton註解標註一個創建類實例的方法,該創建類實例的方法就可以創建一個唯一的類實例。

我對PerActivity和Singleton這些魔法性的註解產生了好奇,同時也產生了迷惑?迷惑是:

  • 自定義Scope註解到底是怎麼工作的
  • 自定義的註解應該怎麼定義名字,是不是定義一個名字就可以達到相應名字的效果。比如Singleton就可以實現單例,PerActivity就可以創建的類實例與Activity“共生死“,是不是我定義一個PerFragment的註解,同樣可以達到創建的類實例就與Fragment“共生死“。大家別對我這幼稚的想法千萬別見笑,當時我就把dagger2的Scope註解想的如此神通廣大了

於是乎我在網上進行各種搜索,並且分析源碼,最後的得到的結果也是讓我大吃一驚。自定義的Singleton、PerActivity註解根本就沒有這些功能。所以也可以說我被Scope坑了,或者是由於自己沒有對Scope有一個深入的理解,被自己坑了。這先賣個關子,後面會具體介紹Scope。

Component組織方式重點中的重點

爲什麼說Component組織方式是重點中的重點呢?因爲前面的各種概念都是在做鋪墊工作,現在我們會從一個app的角度來把這些概念融合在一起。

一個app中應該根據什麼來劃分Component?

假如一個app(app指的是Android app)中只有一個Component,那這個Component是很難維護、並且變化率是很高,很龐大的,就是因爲Component的職責太多了導致的。所以就有必要把這個龐大的Component進行劃分,劃分爲粒度小的Component。那劃分的規則這樣的:

  • 要有一個全局的Component(可以叫ApplicationComponent),負責管理整個app的全局類實例(全局類實例整個app都要用到的類的實例,這些類基本都是單例的,後面會用此詞代替)
  • 每個頁面對應一個Component,比如一個Activity頁面定義一個Component,一個Fragment定義一個Component。當然這不是必須的,有些頁面之間的依賴的類是一樣的,可以公用一個Component。

第一個規則應該很好理解,具體說下第二個規則,爲什麼以頁面爲粒度來劃分Component?

  • 一個app是由很多個頁面組成的,從組成app的角度來看一個頁面就是一個完整的最小粒度了。
  • 一個頁面的實現其實是要依賴各種類的,可以理解成一個頁面把各種依賴的類組織起來共同實現一個大的功能,每個頁面都組織着自己的需要依賴的類,一個頁面就是一堆類的組織者。
  • 劃分粒度不能太小了。假如使用mvp架構搭建app,劃分粒度是基於每個頁面的m、v、p各自定義Component的,那Component的粒度就太小了,定義這麼多的Component,管理、維護就很非常困難

所以以頁面劃分Component在管理、維護上面相對來說更合理。

Singleton沒有創建單例的能力

爲什麼要談到創建單例呢?因爲上面談到一個app要有一個全局的Component(我們暫且叫ApplicationComponent),ApplicationComponent負責管理整個app用到的全局類實例,那不可否認的是這些全局類實例應該都是單例的,那我們怎麼才能創建單例?

上一節提到過Module的作用,Module和Provides是爲解決第三方類庫而生的,Module是一個簡單工廠模式,Module可以包含創建類實例的方法

現在Modlule可以創建所以類的實例。同時

Component會首先從Module維度中查找類實例,若找到就用Module維度創建類實例,並停止查找Inject維度。否則纔是從Inject維度查找類實例。所以創建類實例級別Module維度要高於Inject維度。

所以利用以上2點,我們就可以創建單例。

  • 在Module中定義創建全局類實例的方法
  • ApplicationComponent管理Module
  • 保證ApplicationComponent只有一個實例(在app的Application中實例化ApplicationComponent)

dagger2中正真創建單例的方法就是上面的步驟,全局類實例的生命週期也和Application一樣了,很關鍵的一點就是保證ApplicationComponent是隻初始化一次。那估計有朋友就會問Singleton那豈不是多餘的?
答案當然是 no no no。Singleton有以下作用:

  • 更好的管理ApplicationComponent和Module之間的關係,保證ApplicationComponent和Module是匹配的。若ApplicationComponent和Module的Scope是不一樣的,則在編譯時報錯。
  • 代碼可讀性,讓程序猿更好的瞭解Module中創建的類實例是單例。

組織Component

我們已經把一個app按照上面的規則劃分爲不同的Component了,全局類實例也創建了單例模式。問題來了其他的Component想要把全局的類實例注入到目標類中該怎麼辦呢?這就涉及到類實例共享的問題了,因爲Component有管理創建類實例的能力。因此只要能很好的組織Component之間的關係,問題就好辦了。具體的組織方式分爲以下3種:

依賴方式
一個Component是依賴於一個或多個Component,Component中的dependencies屬性就是依賴方式的具體實現

包含方式
一個Component是包含一個或多個Component的,被包含的Component還可以繼續包含其他的Component。這種方式特別像Activity與Fragment的關係。SubComponent就是包含方式的具體實現。

繼承方式
官網沒有提到該方式,具體沒有提到的原因我覺得應該是,該方式不是解決類實例共享的問題,而是從更好的管理、維護Component的角度,把一些Component共有的方法抽象到一個父類中,然後子Component繼承。

Scope真正用武的時候了

前面也提到Scope的一些基本概念,那Scope的真正用處就在於Component的組織。

  • 更好的管理Component之間的組織方式,不管是依賴方式還是包含方式,都有必要用自定義的Scope註解標註這些Component,這些註解最好不要一樣了,不一樣是爲了能更好的體現出Component之間的組織方式。還有編譯器檢查有依賴關係或包含關係的Component,若發現有Component沒有用自定義Scope註解標註,則會報錯。
  • 更好的管理Component與Module之間的匹配關係,編譯器會檢查 Component管理的Modules,若發現標註Component的自定義Scope註解與Modules中的標註創建類實例方法的註解不一樣,就會報錯。
  • 可讀性提高,如用Singleton標註全局類,這樣讓程序猿立馬就能明白這類是全局單例類。

app的結構.png

總結

關於dagger2概念性的東西基本都已經介紹完畢,剩下的比如Lazy、Provide等註解就不做介紹了,它們太簡單了。同時也着重介紹了Scope,Qualifier等概念。還從整個app的角度來分析Component的組織方式。希望對大家能有幫助,因爲dagger2上手還是比較複雜的,其實關鍵一點就是對於各種概念性的東東不瞭解,不知道它們到底有啥用途。所以我希望能幫到初學者對dagger2有一個整體性概念性的瞭解,然後在看網上的例子時能神清氣爽。



文/牛曉偉(簡書作者)
原文鏈接:http://www.jianshu.com/p/1d42d2e6f4a5
著作權歸作者所有,轉載請聯繫作者獲得授權,並標註“簡書作者”。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章