前言
我在簡(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è):
- 什么是Scope茴肥,當(dāng)你定義Scope時(shí),你在定義什么荡灾?
- 你是否真的需要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ì)象圖包含有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ì)象葵萎。
如上圖所示导犹,右側(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)行聲明/暴露浸间。
如上圖所示太雨,右側(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中的單例是如何存在的。
如上圖所示桂肌,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完全是多余的村怪。
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的使用陷入到這樣的尷尬:
- 多數(shù)情況下我們不需要在Activity斑举、Service、Fragment的范圍內(nèi)提供單例病涨。
- 假設(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é)
- Component就是個(gè)對(duì)象圖更胖,Scope是給Component起得名字铛铁,目的是為了構(gòu)建Component之間的依賴、繼承關(guān)系却妨,這些關(guān)系體現(xiàn)了Dagger對(duì)于依賴注入的分層管理饵逐。
- Module只能提供unscope或者和Component相同Scope的對(duì)象,這是因?yàn)楦鱾€(gè)Component管理各自的對(duì)象圖彪标,別的Component無(wú)法插手倍权。
- 單例對(duì)象被保存在對(duì)應(yīng)的Component中,這個(gè)Component的存活范圍就是單例的作用域捞烟,跟Scope叫什么名字沒(méi)有關(guān)系薄声。
- 當(dāng)你新建SubComponent/Dependent Component時(shí),多問(wèn)問(wèn)自己有沒(méi)有必要坷襟。