當(dāng)定義Dagger2 Scope時(shí),你在定義什么沸版?

前言

我在簡(jiǎn)書(shū)上寫(xiě)得第一篇文章是關(guān)于Dagger2在Android平臺(tái)上新的使用方式的嘁傀,見(jiàn)Dagger2在Android平臺(tái)上的新姿勢(shì)。當(dāng)時(shí)覺(jué)得“真香”视粮。

真香

之后寫(xiě)了一篇介紹了這種新的使用方式背后是如何實(shí)現(xiàn)的细办,見(jiàn)Dagger2在Android平臺(tái)上的新魔法。也是這篇文章讓我重新思考dagger.android是否真的給開(kāi)發(fā)帶了便捷蕾殴,從那之后我便放棄使用dagger.android笑撞。真諷刺,因?yàn)椴涣私庥X(jué)得“真香”钓觉,因?yàn)榱私舛艞墶?br> 這里面的核心問(wèn)題有兩個(gè):

  1. 什么是Scope茴肥,當(dāng)你定義Scope時(shí),你在定義什么荡灾?
  2. 你是否真的需要SubComponent/Dependent Component瓤狐?

1. 什么是Component?

要想解釋什么是Scope批幌,必須先明白什么是Component础锐。Component說(shuō)白了其實(shí)就是一個(gè)對(duì)象圖(object graph),它包含了很多對(duì)象逼裆,以及對(duì)象之間的依賴關(guān)系郁稍,所以我們才能通過(guò)Component直接注入我們需要的對(duì)象,而不需要考慮依賴關(guān)系的問(wèn)題胜宇。

Component就是對(duì)象圖

如上圖所示耀怜,Component的對(duì)象圖包含有5個(gè)對(duì)象:A、B桐愉、C财破、D、E从诲。這些對(duì)象之間有依賴關(guān)系左痢,通過(guò)Component可以直接注入(也可以理解為獲取)這5個(gè)對(duì)象中的任意一個(gè)/幾個(gè),不需要考慮這些對(duì)象之間的依賴關(guān)系俊性,因?yàn)镈agger已經(jīng)幫我們構(gòu)建出了這么一個(gè)對(duì)象圖略步。例如,需要注入A對(duì)象定页,需要先構(gòu)建C和D兩個(gè)對(duì)象趟薄,我們不需要關(guān)心這些,直接注入A對(duì)象即可典徊。
Component是一個(gè)對(duì)象圖杭煎,然而,Component本身也是一個(gè)對(duì)象(“面向?qū)ο蟆敝心莻€(gè)對(duì)象)卒落,不同Component之間也有關(guān)聯(lián)羡铲,類比“面向?qū)ο蟆敝械恼f(shuō)法,Component也有“組合”和“繼承”儡毕。

1.1 Component間的依賴關(guān)系

所謂Component的“組合”是指Component之間的“依賴”關(guān)系也切,對(duì)應(yīng)于Dagger中的Component dependencies。一個(gè)Component可以依賴另外一個(gè)/幾個(gè)Component妥曲,前面已經(jīng)說(shuō)過(guò)贾费,所謂Component也就是對(duì)象圖而已,一個(gè)Component依賴另外的Component檐盟,換一種說(shuō)法也就是褂萧,這個(gè)Component除了自己的對(duì)象圖外還包含有別的Component的“部分”對(duì)象圖。這里的“部分”指的是那些已經(jīng)在別的Component中被聲明/暴露出來(lái)的對(duì)象葵萎。

Component間的依賴

如上圖所示导犹,右側(cè)的Component依賴于左側(cè)的Component,也就是說(shuō)右側(cè)Component的對(duì)象圖不止包含有對(duì)象D羡忘、E谎痢,還包含有左側(cè)Component聲明/暴露出來(lái)的C對(duì)象,所以D對(duì)象可以依賴于C對(duì)象卷雕,但是不能依賴于A或者B對(duì)象节猿。

1.2 Component間的父子關(guān)系

所謂Component的“繼承”是指Component之間的“父子”關(guān)系,對(duì)應(yīng)于Dagger中的SubComponent漫雕。不同于上面提到的Component間的依賴關(guān)系滨嘱,SubComponent繼承了父Component中的所有對(duì)象圖,不需要父Component進(jìn)行聲明/暴露浸间。

Component間的繼承

如上圖所示太雨,右側(cè)SubComponent的對(duì)象圖包含了左側(cè)父Component的所有對(duì)象,不管這些對(duì)象是否在父Component中被聲明/暴露魁蒜,所以對(duì)象D可以依賴于對(duì)象C和對(duì)象B囊扳。需要注意的是吩翻,左側(cè)父Component也可以是SubComponent,所以說(shuō)锥咸,右側(cè)的SubComponent可能不僅包含了父Component的對(duì)象圖狭瞎,還包含了爺Component的對(duì)象圖,等等她君。但是這并不重要脚作,還是可以簡(jiǎn)單的說(shuō)SubComponent繼承了父Component的對(duì)象圖葫哗,至于父Component的對(duì)象圖是完全自己定義的缔刹,還是繼承自別的什么Component,這對(duì)于當(dāng)前的SubComponent而言并不重要劣针。

2. 什么是Scope校镐?

上面介紹了什么是Component,這和Scope有什么關(guān)系呢捺典?答案很簡(jiǎn)單鸟廓,Scope就是Component的名字而已。
眾所周知襟己,@Singleton也是一個(gè)Scope(如果你還不知道引谜,就假裝自己已經(jīng)知道了)。這就奇怪了擎浴,既然@Singleton也是一個(gè)Scope员咽,這和我們自己定義的Scope有什么區(qū)別,為什么使用@Singleton就可以實(shí)現(xiàn)全局單例贮预,而我們自定義一個(gè)@ActivityScope或者@PerActivity就只能實(shí)現(xiàn)一個(gè)在每個(gè)Activity中的單例贝室,同理也適用于@FragmentScope或者@PerFragment?這是Dagger2最微妙的部分仿吞,也是讓很多初學(xué)者困惑的地方滑频,我當(dāng)初學(xué)習(xí)Dagger2的時(shí)候就有這樣的困惑。先直接拋出答案唤冈,其實(shí)峡迷,Dagger2沒(méi)有這樣的魔法,自定義的@ActivityScope你虹,@FragmentScope@Singleton沒(méi)有什么區(qū)別绘搞,單例的作用域取決于Component自身的存活范圍。

2.1 Scope就是Component的名字

前面介紹了Component之間有依賴關(guān)系和父子關(guān)系售葡,建立這些關(guān)系的前提是每個(gè)Component都得有個(gè)“名字”看杭。而這個(gè)名字就是Scope。

沒(méi)有“名字”(不定義Scope)的Component之間也可以建立這兩種關(guān)聯(lián)挟伙,但是實(shí)際使用當(dāng)中沒(méi)有太大作用楼雹,所以你可以簡(jiǎn)單地認(rèn)為Component必須定義自己的Scope模孩。

那么為什么必須給Component起個(gè)名字呢?最核心的原因在于要給單例提供作用域贮缅。依賴注入繞不開(kāi)問(wèn)題就是如何提供單例對(duì)象榨咐,以及這個(gè)單例對(duì)象的使用范圍(作用域)。Dagger2解決這兩個(gè)問(wèn)題的關(guān)鍵就在于Scope谴供。
之前介紹Component的對(duì)象圖時(shí)都忽略了單例的情況块茁,下面仔細(xì)看看Component中的單例是如何存在的。

Singleton

如上圖所示桂肌,D對(duì)象是個(gè)單例数焊,而C對(duì)象不是,每次需要C對(duì)象是都會(huì)新建一個(gè)崎场,而D對(duì)象在該Component中始終只有一個(gè)佩耳,這是因?yàn)椋瑔卫龑?duì)象D被存儲(chǔ)在了該Component中谭跨,只要能獲得該Component干厚,就能獲得單例對(duì)象D。
并且螃宙,Dagger還規(guī)定了蛮瞄,Component中使用的Module只能提供unscope的對(duì)象(普通對(duì)象,每次需要時(shí)都新建)谆扎,或者跟該Component Scope一致的對(duì)象挂捅。例如,上圖中燕酷,Component定義的Scope是@Singleton籍凝,那么所有Module就只能提供unscope的對(duì)象C,或者@Singleton的對(duì)象D苗缩。之所以這么規(guī)定饵蒂,是因?yàn)槊總€(gè)Component定義了自己的Scope,并管理著自己的對(duì)象圖酱讶,與之關(guān)聯(lián)的Module提供這個(gè)Scope內(nèi)的單例或者unscope就可以了退盯,不能通過(guò)這個(gè)Component向別的Component的作用域提供單例,因?yàn)槟敲磩e的Component的事情泻肯,與你無(wú)關(guān)渊迁。
為什么我說(shuō)Scope就是Component的名字而已?因?yàn)檫@個(gè)Component的Scope可以是任意Scope灶挟,可以是默認(rèn)的@Singleton琉朽,也可以是自定義的@ActivityScope,或者任意別的自定義Scope都可以稚铣。這個(gè)Scope并不決定單例的范圍箱叁,它只是一個(gè)標(biāo)識(shí)墅垮,并且強(qiáng)制要求該Component使用到的Module都遵守這個(gè)標(biāo)識(shí),不能越界耕漱。通過(guò)這個(gè)標(biāo)識(shí)標(biāo)注的單例都被存儲(chǔ)到了該Component中算色,只要能獲得該Component,你就能獲得這些單例螟够,單例的范圍/作用域的關(guān)鍵在于該Component的存活范圍灾梦。在Application中保存了該Component,那么就是全局單例的妓笙;在Activity中保存了該Component若河,那么就是在Activity范圍內(nèi)的單例,等等给郊。跟定義的Scope叫什么沒(méi)有一毛錢(qián)關(guān)系牡肉。

雖說(shuō)Scope的名字可以任意取,但是也不能瞎取淆九。在程序中起名字最重要的就是表意。所以我們經(jīng)常去@ActivityScope,@FragmentScope這樣的名字毛俏,但是單例的作用域跟這些名字之間并沒(méi)有任何關(guān)系炭庙,并不是因?yàn)槟愣x的Scope叫@ActivityScope,所以單例的作用域就被限定在了Activity的范圍內(nèi)煌寇。就好像焕蹄,你養(yǎng)了一只狗,你非得給他取名叫“喵咪”阀溶,也不能說(shuō)你錯(cuò)腻脏,但是大家肯定覺(jué)得你有毛病。反過(guò)來(lái)银锻,即使他叫“喵咪”永品,他也仍然是一只狗。

3. 是否需要SubComponent/Dependent Component

假設(shè)你平時(shí)做Android開(kāi)發(fā)時(shí)會(huì)為每個(gè)Activity和Fragment定義相應(yīng)的Component(例如使用dagger.android)击纬。當(dāng)你明白了什么是Component鼎姐,什么是Scope之后,你有沒(méi)有想過(guò)這么一個(gè)問(wèn)題更振,這些定義的Component是否有必要炕桨。
如前所述,定義Scope就是給Component起名字肯腕,而給Component起名字主要是為了構(gòu)建多個(gè)Component之間的關(guān)聯(lián)献宫。試問(wèn),構(gòu)建這些關(guān)聯(lián)的目的是什么实撒?當(dāng)然是為了分層管理姊途,在不同作用域內(nèi)共享一些單例帖池。如果你定義了一個(gè)SubComponent/Dependent Component,與之關(guān)聯(lián)的所有Module卻并沒(méi)有向它提供單例(所有Module全部提供的是unscope對(duì)象)吭净,那么這個(gè)SubComponent/Dependent Component是沒(méi)必要的睡汹,你完全可以使用上一層的Component(父Component/被依賴的Component)去完成相同的任務(wù),除非你確實(shí)需要為這個(gè)Component提供這些Module寂殉,以對(duì)對(duì)象進(jìn)行分層管理囚巴。甚至,更極端的情況友扰,這個(gè)SubComponent/Dependent Component完全不需要Module彤叉,在這種情況下,使用這個(gè)SubComponent/Dependent Component完全是多余的村怪。

多余的Component

3.1 為什么放棄dagger.android

上面這張圖就是我放棄dagger.android的原因是秽浇,因?yàn)槲野l(fā)現(xiàn)自己之前使用dagger.android時(shí)就是這樣的,完全沒(méi)有為SubComponent提供新的Module甚负,因此我現(xiàn)在已經(jīng)不再使用dagger.andoird柬焕。
dagger.android在使用上有其限制,主要表現(xiàn)在不能向Module提供參數(shù)(注意這里說(shuō)的是dagger.android)梭域。這使得dagger.android的使用陷入到這樣的尷尬:

  1. 多數(shù)情況下我們不需要在Activity斑举、Service、Fragment的范圍內(nèi)提供單例病涨。
  2. 假設(shè)我們需要在某個(gè)Activity范圍內(nèi)提供某個(gè)單例富玷,以實(shí)現(xiàn)在它的Fragment中共享該單例,但是卻發(fā)現(xiàn)不能為Module提供參數(shù)既穆,因?yàn)槎鄶?shù)情況下都是需要通過(guò)參數(shù)來(lái)創(chuàng)建Activity范圍內(nèi)的單例赎懦。

第一種情形下,dagger.android顯得多余幻工,徒增麻煩励两。第二種情形下,dagger.android根本無(wú)法使用会钝。

3.2 重新審視你的Component

在我看來(lái)伐蒋,在Android平臺(tái)上很多SubComponent/Dependent Component是沒(méi)有必要的,多數(shù)情況下迁酸,我們需要的單例是全局的先鱼,把這個(gè)全局的Component保存在Application中即可。并不經(jīng)常需要在Activity奸鬓、Fragment范圍內(nèi)共享什么單例焙畔。如果你使用MVP模式,那么有時(shí)確實(shí)需要在Activity范圍內(nèi)去共享某個(gè)Presenter串远,但是也沒(méi)必要為每個(gè)Activity宏多、Fragment創(chuàng)建Component儿惫。這種誤用往往是因?yàn)椴涣私釪agger,把這種使用方式當(dāng)成一種范式伸但,仿佛只有這樣Dagger才能正常運(yùn)行肾请,其實(shí)完全不是這樣的。

4. 總結(jié)

  1. Component就是個(gè)對(duì)象圖更胖,Scope是給Component起得名字铛铁,目的是為了構(gòu)建Component之間的依賴、繼承關(guān)系却妨,這些關(guān)系體現(xiàn)了Dagger對(duì)于依賴注入的分層管理饵逐。
  2. Module只能提供unscope或者和Component相同Scope的對(duì)象,這是因?yàn)楦鱾€(gè)Component管理各自的對(duì)象圖彪标,別的Component無(wú)法插手倍权。
  3. 單例對(duì)象被保存在對(duì)應(yīng)的Component中,這個(gè)Component的存活范圍就是單例的作用域捞烟,跟Scope叫什么名字沒(méi)有關(guān)系薄声。
  4. 當(dāng)你新建SubComponent/Dependent Component時(shí),多問(wèn)問(wèn)自己有沒(méi)有必要坷襟。

參考

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

什么決定了Component的生命周期

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奸柬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子婴程,更是在濱河造成了極大的恐慌,老刑警劉巖抱婉,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件档叔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蒸绩,警方通過(guò)查閱死者的電腦和手機(jī)衙四,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)患亿,“玉大人传蹈,你說(shuō)我怎么就攤上這事〔脚海” “怎么了惦界?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)咙冗。 經(jīng)常有香客問(wèn)我沾歪,道長(zhǎng),這世上最難降的妖魔是什么雾消? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任灾搏,我火速辦了婚禮挫望,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘狂窑。我一直安慰自己媳板,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布泉哈。 她就那樣靜靜地躺著蛉幸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪旨巷。 梳的紋絲不亂的頭發(fā)上巨缘,一...
    開(kāi)封第一講書(shū)人閱讀 49,760評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音采呐,去河邊找鬼若锁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛斧吐,可吹牛的內(nèi)容都是我干的又固。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼煤率,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼仰冠!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蝶糯,我...
    開(kāi)封第一講書(shū)人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤洋只,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后昼捍,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體识虚,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年妒茬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了担锤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乍钻,死狀恐怖肛循,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情银择,我是刑警寧澤多糠,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站欢摄,受9級(jí)特大地震影響熬丧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一析蝴、第九天 我趴在偏房一處隱蔽的房頂上張望害捕。 院中可真熱鬧,春花似錦闷畸、人聲如沸尝盼。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)盾沫。三九已至,卻和暖如春殿漠,著一層夾襖步出監(jiān)牢的瞬間赴精,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工绞幌, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蕾哟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓莲蜘,卻偏偏與公主長(zhǎng)得像谭确,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子票渠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

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