从 Dagger 到 Hilt,谷歌为何执着于让我们用依赖注入?
來源 | 扔物線
責編 | Carol
文章開始之前,首先來看個視頻:
開始
說到依賴注入,做 Android 的人都會想到一個庫:Dagger;說到 Dagger,大家的反應普遍是一套三連:牛逼、高端、我才不用。
又牛逼又高端,為什么不用?因為太難了。是吧?又難學又難用。大多數的人在學習 Dagger 的路上就被直接勸退了,剩下的這一小撮人最終排除萬難,學會并且用上了 Dagger,但多半都是用著用著就掉進了自己親手用 Dagger 搭建的迷宮里,怎么也繞不清楚,而且越陷越深,就這么成年累月地被它折磨。
有人可能會說:難用就別用唄?拆出來啊。
拆?哼哼。你對 Dagger 一無所知。
而就在上個月,Android 團隊又在 Jetpack 里面又增加了一個新的依賴注入庫:Hilt。這個 Hilt 是專門針對于 Android 平臺的依賴注入庫,它是基于 Dagger 的。
啊?基于……Dagger?這次到底是真正的神器到來,還是又一個大坑?
依賴注入是什么?
Dagger 的名字取自有向無環圖 DAG (directed acyclic graph):
因為程序里的依賴關系拼接起來就是一個或者多個有向無環圖:
DAG-er,Dagger,取了個諧音,Dagger 是匕首的意思。而這次的 Hilt 是刀柄的意思,匕首很難用是吧?來,給你個柄。
說得很好聽,到底有沒有那么好用啊?這是個復雜的問題,且聽我慢慢道來~
依賴注入有什么用
Hilt 好不好用,我們先來看看它是個什么。它是個用注解來進行配置的依賴注入庫。注解是它的寫法,首先它是個依賴注入庫,對吧?什么是依賴注入?一個類里有兩個變量,這兩個變量就是它的依賴:
要初始化一個依賴,有兩種方法:第一,你這個類自己初始化:
第二,讓外部幫你初始化。
其中這第二種,讓外部幫你初始化你的依賴,就叫依賴注入。關鍵在于初始化是誰做的,至于最后一步是你把結果拿過來,還是說你連拿都不用拿,最后一步的賦值工作也讓外部來幫你做了,這都不重要,只要初始化工作是外部做的,就都叫依賴注入。
所以 Factory 的使用是依賴注入嗎?
是的。
Builder?
也是。
帶參數的構造函數?
也是!
這些都屬于由外部來提供依賴的初始化,所以都是依賴注入,并不是非要像 Dagger 那樣使用注解的像魔法一樣的才叫依賴注入。也就是說,其實我們每個人都已經在使用依賴注入了。雖然很多人在面對 Dagger 的時候會問「依賴注入到底有什么用」,但其實 Dagger 并不是提供了依賴注入的能力,而是為依賴注入提供了一種更簡單的方式。依賴注入本來就是有用的,這個問題不想明白,不管是 Dagger 還是現在的 Hilt,你都用不好。
Dagger 讓我們可以用注解的方式來配置依賴關系,讓依賴注入變得更方便。不過由于功能復雜,導致它的上手非常困難;再加上剛才我說的,很多人對于依賴注入的作用以及 Dagger 的定位都沒搞清楚,這兩個原因加起來,就導致很多人還沒學會 Dagger 就把它棄了,讓 Dagger 成為 Android 史上最受冷落的優質庫。這樣的結果不論是對 Dagger 還是對我們,都是很可惜的。
而 Hilt 的出現,就直接解決了 Dagger 太復雜的這個問題。
Hilt 怎么幫助我們進行依賴注入
Hilt 是 Google 專門針對 Android 平臺做的一個依賴注入庫。它不是從里到外全新開發的,而是基于 Dagger 做的,它的下層還是 Dagger。
為什么不直接去優化改進 Dagger,而要基于它做一個新庫呢?因為 Hilt 做的事其實也并不是對 Dagger 進行優化,而是場景化:針對 Android 開發制定了一系列的規則,通過這些規則大大簡化了這套工具的使用。例如在 Dagger 里,你要對某個類的依賴進行注入,你需要手動獲取依賴圖和執行注入依賴操作:
而在 Hilt 里,注入會自動完成:
因為 Hilt 會自動找到 Android 的系統組件里面那些最佳的初始化位置——比如 Activity 的 onCreate() ——然后在這些位置注入依賴。所以,為什么不是去優化 Dagger,而是做了個新庫?因為 Hilt 本身并不是一種優化,而是場景化,或者說,它是一種針對場景的優化。總之,它是不通用的,只能給 Android 用,所以不能放在 Dagger 里。
有點明白了吧?
那它具體怎么用呢?大概是這樣的:
我們程序里有些對象是全局共享的,比如線程池,或者 Retrofit 對象,這種東西我們通常會把它放在 Application 對象里,或者做成單例的:
而如果用 Hilt,你也可以把它做成自動注入的依賴:
還有些對象是局部共享的,比如某個 Activity 會把一些顯示用的數據共享給它內部的一些 View 和 Fragment。這一類情況我們的做法通常是獲取外部 Activity 對象然后強轉,再去拿它內部的對象:
而如果用 Hilt,你可以把這個對象直接聲明出來,讓它自動注入:
這不只是一個「美觀」的差別,依賴注入可以讓你的程序更加靈活,比如如果你的 View 可以在多個不同的 Activity 里顯示,那你在 View 里面要怎么強轉?你要轉成誰?
很麻煩,是吧?而如果用依賴注入,這些就都是自動的。
除了共享的對象,不共享的也可以用依賴注入的方式來進行初始化,因為依賴注入的作用除了對共享對象提供一致性支持,也可以讓我們在創建任何對象的時候省一些思考和力氣:
@Inject?newUser:?User總之,如果一個組件可能會被被共享,或者不會被共享但可能會在多處使用,你都可以使用 Hilt 來把它配置成依賴注入的加載方式。
加載的方式可以選擇直接調用構造函數:
或者指定子類或實現類:
或者干脆給出具體的代碼:
加載的作用域可以選擇默認的每次都初始化,也可以設置成全局單例的:
也可以設置成針對任何 Activity、Fragment、View 或者 ViewModel 的局部共享:
簡單又強大,好用又靈活。具體的寫法你可以去看文檔,或者過段時間我會有一次公開課,到時候也會提前通知大家。
到這里有的人可能會分個叉可能會想:誒 ButterKnife 或者現在 Jetpack 推出的 ViewBinding 它們提供的功能,Hilt 提供了嗎?因為如果提供了,我在用了 Hilt 之后,不就可以把 ButterKnife 和 ViewBinding 扔掉了?
不好意思,Hilt 不提供它們的功能。Hilt 和 Dagger 雖然用法和 ButterKnife 很像,都是給變量加注解,然后變量會自動賦值,但它們的功能定位是不一樣的:Hilt 和 Dagger 是做依賴注入的,而 ButterKnife 和 ViewBinding 是做視圖綁定的。
這可不是個文字游戲,依賴注入和視圖綁定是有本質區別的:依賴注入是由外部對對象進行初始化,也就是所謂的控制翻轉;而視圖綁定是讓變量去指向一個已經有了的 View,它的依賴依然是由依賴持有者自己決定的,這是一個本質的區別。
Dagger 為什么難用
這么看來,Hilt 還是很好用的,是吧?那有些人就又有問題了:哎,Hilt 這么好用,那Dagger 真的難用嗎?到底難用在哪了?
其實說白了,Dagger 的難用主要在于這個框架太強大和靈活了,導致你要遵守很多約定才能正確使用它。比如在 Hilt 里,一個注解就能讓 Activity 內部的依賴自動被注入,而 Dagger 需要手動注入;再比如在 Hilt 里如果你想讓一個對象只在 Activity 內部被共享而不是全局共享,也就是一個注解能解決的問題,而在 Dagger 里面你需要先去創建一個自定義的注解。這些難嗎?每個都不難的,對吧?但把它們放在一起,讓你靈活搭配使用,就有點難了。
另外,Dagger 被大家普遍認為難的另一個原因剛才我也說過了:很多人連依賴注入都不太懂的。所以我再說一遍:如果一個組件可能被共享,或者可能在多處被使用,你可以使用依賴注入來初始化它。然后,在需要依賴注入的場景里,使用 Dagger 能讓你的依賴注入寫起來更簡單。最后,Hilt 進一步簡化了這個事情。先知道它是什么,再去用它。
總結
?
所以今天表面上是在介紹 Hilt,其實是對于 Hilt 以及它背后的依賴注入機制進行一個整體的講解,希望對你可以有幫助。大家學知識和技術的時候,一定不要只關注表面,要透過表面看到里面的本質,掌握最核心的東西。
那么回到這期的標題——《從 Dagger 到 Hilt,谷歌為何執著于讓我們用依賴注入》,為什么?其實谷歌并沒有非要讓我們使用依賴注入,而是我們本來就需要使用依賴注入,谷歌只是想提供一種更方便的方式讓我們去使用依賴注入而已。Dagger 很強大,但太難學從而導致太難用;而 Hilt 徹底掃除了這個障礙,那……
要不咱給它個機會?
更多閱讀推薦
閑魚的云原生故事:靠什么支撐起萬億的交易規模?
野雞大學怎么知道考生電話的?
達摩院NLP團隊斬獲六項世界冠軍背后,讓AI沒有難懂的語言
我把這篇文章給女朋友看,她終于明白什么是「數據中臺」了
云交易所已成資金盤、殺豬盤重災區,曾被寄予厚望,如今罪惡叢生
總結
以上是生活随笔為你收集整理的从 Dagger 到 Hilt,谷歌为何执着于让我们用依赖注入?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在 Kubernetes 上配置 J
- 下一篇: 华为在中国建立其全球最大的网络安全透明中