1. 程式人生 > >dagger2 讓你愛不釋手:重點概念講解、融合篇

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

前言

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有一個整體性概念性的瞭解,然後在看網上的例子時能神清氣爽。

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

任選一種支付方式