Android |《看完不忘系列》之dagger

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"嗨,我是哈利迪~《看完不忘系列》將以"},{"type":"codeinline","content":[{"type":"text","text":"從樹幹到細枝"}]},{"type":"text","text":"的思路分析一些技術框架,本文將對開源項目"},{"type":"codeinline","content":[{"type":"text","text":"dagger"}]},{"type":"text","text":"進行介紹。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文約3800字,閱讀大約10分鐘。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Dagger源碼基於最新版本2.28.3"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/33/33c3322789466e3b3b6cab1534668570.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"背景"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"依賴注入(Dependency Injection,DI)遵循控制反轉(Inversion of Control,IoC)原則,簡單來說就是"},{"type":"codeinline","content":[{"type":"text","text":"創建對象時給對象傳入依賴"}]},{"type":"text","text":",通過傳入不同實例來實現不同行爲(控制),比如常見的構造方法和setter都叫注入。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"簡單概括一下谷歌的"},{"type":"link","attrs":{"href":"https://developer.android.google.cn/training/dependency-injection","title":""},"content":[{"type":"text","text":"造車栗子"}]},{"type":"text","text":","}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一、不注入,由Car類自己創建依賴的Engine實例,當需要替換汽車引擎時,需要修改Car類,違背了開放封閉原則,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"class Car {\n private Engine engine = new Engine();\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"二、手動依賴注入,如構造方法和setter,當需要替換汽車引擎時,傳入不同的引擎實現(如ElectricEngine extends Engine)即可,無需修改Car類,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//1.構造方法注入\nclass Car {\n private final Engine engine;\n \n public Car(Engine engine) {\n this.engine = engine;\n }\n}\n\n//2.setter注入\nclass Car {\n private Engine engine;\n\n public void setEngine(Engine engine) {\n this.engine = engine;\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"三、隨着汽車配件變多,依賴注入的代碼也會越來越多,如汽車需要引擎、輪胎,引擎又需要氣缸和火花塞,這樣一層層下去注入代碼成指數級上升,出現了大量創建對象的樣板代碼,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d3/d3005822e48f49ba95657437e170cb5b.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"class Car {\n private final Engine engine;//引擎\n private final Wheel wheel;//輪胎\n\n public Car(Engine engine,Wheel wheel) {\n this.engine = engine;\n this.wheel = wheel;\n }\n}\n\nclass Engine {\n private final Cylinder cylinder;//氣缸\n private final SparkPlug sparkPlug;//火花塞\n\n public Car(Cylinder cylinder,SparkPlug sparkPlug) {\n this.cylinder = cylinder;\n this.sparkPlug = sparkPlug;\n }\n}\n\nvoid main(){\n //爲了造輛車,要new出一堆對象,還要構造方法注入,套娃既視感\n Cylinder cylinder = new Cylinder();\n SparkPlug sparkPlug = new SparkPlug();\n Engine engine = new Engine(cylinder,sparkPlug);\n Wheel wheel = new Wheel();\n Car car = new Car(engine,wheel);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從代碼可以看出有兩大痛點,一是要創建"},{"type":"codeinline","content":[{"type":"text","text":"很多對象"}]},{"type":"text","text":",二是對象的創建過程還需"},{"type":"codeinline","content":[{"type":"text","text":"有序"}]},{"type":"text","text":",即要先有氣缸和火花塞才能造引擎,先有引擎和輪胎才能造車,這樣使用方還需"},{"type":"codeinline","content":[{"type":"text","text":"花時間瞭解配件間的依賴關係"}]},{"type":"text","text":",很是不便。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"於是就有了一些庫來實現自動依賴注入,有兩個實現思路(koin的實現以後再聊~),"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"一是運行期反射連接依賴項,編譯影響小,但運行慢"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"二是編譯期就連接依賴項,創建輔助類需要額外的io和編譯耗時,會拖慢編譯速度,但運行快"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"像Android內存和算力都有限的終端設備,dagger當然是選擇思路2啦。dagger通過"},{"type":"codeinline","content":[{"type":"text","text":"註解"}]},{"type":"text","text":"標記"},{"type":"codeinline","content":[{"type":"text","text":"對象創建姿勢"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"依賴關係"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"作用域"}]},{"type":"text","text":"等信息,在編譯期創建輔助類,從而實現自動依賴注入。不過dagger的上手成本略高,谷歌後來又推出了Hilt,旨在讓我們用得"},{"type":"text","marks":[{"type":"del"}],"text":"舒心"},{"type":"text","text":","}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://developer.android.google.cn/training/dependency-injection/hilt-android","title":""},"content":[{"type":"text","text":"Hilt"}]},{"type":"text","text":" 是推薦用於在 Android 中實現依賴項注入的 Jetpack 庫。Hilt 通過爲項目中的每個 Android 類提供容器並自動爲您管理其生命週期,定義了一種在應用中執行 DI 的標準方法。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Hilt 在熱門 DI 庫 "},{"type":"link","attrs":{"href":"https://developer.android.google.cn/training/dependency-injection/dagger-basics","title":""},"content":[{"type":"text","text":"Dagger"}]},{"type":"text","text":" 的基礎上構建而成,因而能夠受益於 Dagger 提供的編譯時正確性、運行時性能、可伸縮性和 Android Studio 支持。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"-- 谷歌"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Hilt就先放一放,下面我們先開始dagger之旅吧~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"補:關於手動注入的痛點,可以看下谷歌的"},{"type":"link","attrs":{"href":"https://developer.android.google.cn/training/dependency-injection/manual","title":""},"content":[{"type":"text","text":"手動依賴項注入"}]},{"type":"text","text":"(看完或許能更好的理解dagger的設計)。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"樹幹"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"簡單使用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"依賴,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"groovy"},"content":[{"type":"text","text":"implementation 'com.google.dagger:dagger:2.28.3'\nannotationProcessor 'com.google.dagger:dagger-compiler:2.28.3'"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"@Inject和@Component"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"@Inject標記實例的創建姿勢,汽車和引擎類,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"class Car {\n private final Engine mEngine;\n\n @Inject //告訴dagger如何創建Car\n public Car(Engine engine) {\n mEngine = engine;\n }\n\n public void start() {\n mEngine.start();\n }\n}\n\nclass Engine {\n @Inject //告訴dagger如何創建Engine\n public Engine() {\n }\n\n public void start() {\n Log.e(\"哈利迪\", \"引擎發動,前往秋名山\");\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣dagger就能生成類似 new Car(new Engine()) 的代碼來創建實例,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"@Component標記所要創建的實例有哪些,如在造車圖紙(接口)裏聲明要造車,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Component //告訴dagger要造啥\ninterface CarGraph {\n //造車\n Car makeCar();\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"make一下項目,生成Car"},{"type":"text","marks":[{"type":"italic"}],"text":"Factory、Engine"},{"type":"text","text":"Factory和DaggerCarGraph三個類,在Activity中使用,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"class DaggerActivity extends AppCompatActivity {\n\n @Override\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_dagger);\n //得到造車圖紙\n CarGraph carGraph = DaggerCarGraph.create();\n //造出一輛汽車\n Car car = carGraph.makeCar();\n //引擎發動\n car.start();\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注入姿勢二,還可以直接把汽車注入給Activity,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Component //告訴dagger要造啥\npublic interface CarGraph {\n //造車\n Car makeCar();\n\n //新增:告訴dagger有個Activity要注入\n void inject(DaggerActivity activity);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"make一下,這時會多出一個類DaggerActivity_MembersInjector(成員注入器),我們後面再看,在Activity中,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"class DaggerActivity extends AppCompatActivity {\n //向Activity注入汽車\n @Inject\n Car mCar;\n\n @Override\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_dagger);\n //得到造車圖紙\n CarGraph carGraph = DaggerCarGraph.create();\n //告訴dagger有個Activity要注入\n carGraph.inject(this);\n //此時,mCar已被dagger自動賦值,我們可以直接發動引擎\n mCar.start();\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不管是哪種姿勢,都可以看出,Activity只管提車,無需關心造車內部的細節(用了什麼引擎),這樣以後要換引擎了,Activity和Car也不用改動代碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"@Module和@Binds"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼問題來了,通常我們都是面向接口編程,現在想把Engine換成IEngine接口咋整呢?因爲我有兩種引擎,分別是汽油車的GasEngine,和電動車的ElectricEngine,接口沒有構造方法怎麼注入?此時@Module和@Binds註解就派上用場了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"定義引擎接口和實現類,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"interface IEngine {\n public void start();\n}\n\nclass GasEngine implements IEngine {//汽油引擎\n @Inject\n public GasEngine() {\n }\n\n @Override\n public void start() {\n Log.e(\"哈利迪\", \"汽油 引擎發動,前往秋名山\");\n }\n}\n\nclass ElectricEngine implements IEngine {//電動引擎\n @Inject\n public ElectricEngine() {\n }\n\n @Override\n public void start() {\n Log.e(\"哈利迪\", \"電動 引擎發動,前往秋名山\");\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後Car我們不要了,新寫一個汽車類NewCar,讓他依賴於接口IEngine而非類,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"class NewCar {\n //依賴於接口\n private final IEngine mEngine;\n\n @Inject //告訴dagger如何創建NewCar\n public NewCar(IEngine engine) {\n mEngine = engine;\n }\n\n public void start() {\n mEngine.start();\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面有請@Module和@Binds登場!建一個抽象類GasEngineModule,表示汽油引擎模塊,用於提供汽油引擎,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Module\nabstract class GasEngineModule {//汽油引擎模塊\n @Binds\n abstract IEngine makeGasEngine(GasEngine engine);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後在造車圖紙CarGraph裏支持一下NewCar的創建,把Module引入Component,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Component(modules = {GasEngineModule.class})\n//引入汽油引擎模塊\npublic interface CarGraph {\n //...\n\n //造新車\n NewCar makeNewCar();\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Activity使用,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//DaggerActivity.java\nvoid onCreate(Bundle savedInstanceState) {\n CarGraph carGraph = DaggerCarGraph.create();\n NewCar newCar = carGraph.makeNewCar();\n newCar.start();\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"搞定~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/62/6252b49d409487b4327bfaede65ea35c.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這時如果想要把NewCar換成電動引擎,直接依賴新的模塊即可,新增模塊ElectricEngineModule,用於提供電動引擎,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Module\npublic abstract class ElectricEngineModule {//電動引擎模塊\n @Binds\n abstract IEngine makeElectricEngine(ElectricEngine engine);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GasEngineModule模塊換成ElectricEngineModule,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Component(modules = {ElectricEngineModule.class})//替換\npublic interface CarGraph {\n //...\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"運行就能得到電動車了,可見這個過程我們不需要修改NewCar就能換掉引擎(偷樑換柱)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"@IntoMap和@StringKey"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"又有一天,突然想開雙混動汽車了咋整,就是說汽油引擎和電動引擎我全都要,我們發現@Component的modules可以傳入數組,那試試把兩個引擎模塊都傳進去,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Component(modules = {GasEngineModule.class, ElectricEngineModule.class})\npublic interface CarGraph {\n //...\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"make一下,不用想也知道會出錯,NewCar只有一個依賴IEngine接口,這時dagger已經不知道,造NewCar到底要傳入哪個引擎了,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/10/10f1c6f55b2463f88243419cb9a2f4c1.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那怎麼辦?支持多綁定的@IntoMap和@StringKey登場了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"IntoMap表示會把這個Module存進map裏,StringKey表示Module名字,這個名字也會作爲map的key,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Module\nabstract class GasEngineModule {//汽油引擎模塊\n @Binds\n @IntoMap\n @StringKey(\"Gas\")\n abstract IEngine makeGasEngine(GasEngine engine);\n}\n\[email protected]\nabstract class ElectricEngineModule {//電動引擎模塊\n @Binds\n @IntoMap\n @StringKey(\"Electric\")\n abstract IEngine makeElectricEngine(ElectricEngine engine);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後修改一下NewCar,此時不再只是依賴一個引擎接口IEngine了,而是一組引擎,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"class NewCar {\n //我現在是混動車了,要用map接收引擎\n private final Map mEngineMap;\n\n @Inject //告訴dagger如何創建NewCar\n public NewCar(Map engineMap) {\n mEngineMap = engineMap;\n }\n\n public void start() {\n //雙混動走起~\n for (Map.Entry entry : mEngineMap.entrySet()) {\n entry.getValue().start();\n //汽油 引擎發動,前往秋名山\n //電動 引擎發動,前往秋名山\n }\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Activity代碼不用改動,雙混動車就造好了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/45/45e803043ee0f97aed7e25e146e15797.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"@Singleton"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果想要給雙混動NewCar限量全球一款,可以用@Singleton指定爲單例,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Singleton //單例,我現在是限量款混動車\npublic class NewCar {\n //...\n}\n\[email protected] //造車圖紙也需跟着單例\[email protected](modules = {GasEngineModule.class, ElectricEngineModule.class})\npublic interface CarGraph {\n //...\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Activity運行,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//DaggerActivity.java\nvoid onCreate(Bundle savedInstanceState) {\n CarGraph carGraph = DaggerCarGraph.create();\n NewCar newCar = carGraph.makeNewCar();\n newCar.start();\n NewCar newCar2 = carGraph.makeNewCar();\n newCar2.start();\n //newCar和newCar2是同一個實例\n Log.e(\"哈利迪\", newCar.hashCode() + \",\" + newCar2.hashCode());\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"dagger的使用就先聊到這啦,相信對dagger也已經有了初步認識,還有些註解沒講到,比如:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"@Provides:當我們沒法用@Inject來標記實例的創建姿勢時,可以用@Module和@Provides來提供實例,比如Retrofit是三方庫的類我們沒法標記其構造方法,則可以用Provides提供,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Module\npublic class NetworkModule {\n @Provides\n public Retrofit provideRetrofit() {\n return new Retrofit.Builder()\n .baseUrl(\"xxx\")\n .build();\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"@Scope:作用域 ..."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"@Subcomponent:子組件..."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"註解還有很多,準備放到細枝篇來寫了...🤣"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"實現原理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"dagger編譯期解析註解創建輔助類的過程就不分析了,我們直接看他生成的輔助類,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/aa/aae56f0390d747ac983e86712b32fb0f.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注:一開始寫接口名字時,用造車圖紙CarGraph而不是造車廠CarFactory,是爲了避免和dagger的生成類搞混,用CarGraph有幾何圖的寓意,可以理解成造車藍圖(PPT),讓我們一起,爲夢想窒息..."}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們看到DaggerCarGraph,他實現了我們的CarGraph接口,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"class DaggerCarGraph implements CarGraph {\n //Provider,提供多引擎map,\n private Provider> mapOfStringAndIEngineProvider;\n //Provider,提供NewCar\n private Provider newCarProvider;\n\n private DaggerCarGraph() {\n initialize();\n }\n\n public static Builder builder() {\n return new Builder();\n }\n\n public static CarGraph create() {\n //用builder創建DaggerCarGraph實例\n return new Builder().build();\n }\n\n private void initialize() {\n //創建提供汽油引擎的Provider和電動引擎的Provider\n //用put將他們存起來,合併成一個提供多引擎map的Provider\n this.mapOfStringAndIEngineProvider = MapFactory.builder(2)\n .put(\"Gas\", (Provider) GasEngine_Factory.create())\n .put(\"Electric\", (Provider) ElectricEngine_Factory.create())\n .build();\n //把提供多引擎map的Provider傳入NewCar_Factory,\n //然後用DoubleCheck包一層,他會加鎖、處理單例邏輯\n this.newCarProvider = DoubleCheck.provider(\n NewCar_Factory.create(mapOfStringAndIEngineProvider));\n }\n\n @Override\n public Car makeCar() {\n //1. 老的造車:姿勢一,用makeCar直接造\n return new Car(new Engine());\n }\n\n @Override\n public void inject(DaggerActivity activity) {\n //2. 老的造車:姿勢二,用@Inject注入 ↓\n injectDaggerActivity(activity);\n }\n\n private DaggerActivity injectDaggerActivity(DaggerActivity instance) {\n //2. 老的造車:姿勢二,先創建後注入\n //實例的創建也用makeCar,如果我們接口沒有定義這個方法,dagger會生成一個功能一樣的getCar\n DaggerActivity_MembersInjector.injectMCar(instance, makeCar());\n return instance;\n }\n\n @Override\n public NewCar makeNewCar() {\n //3. 新的造車,從Provider獲取\n return newCarProvider.get();\n }\n\n public static final class Builder {\n private Builder() {\n }\n public CarGraph build() {\n return new DaggerCarGraph();\n }\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1. 老的造車:姿勢一,用makeCar直接造"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在造老車Car時,姿勢一是直接調接口的makeCar方法來造,實現就是簡單的new出實例。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2. 老的造車:姿勢二,用@Inject注入"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"姿勢二用@Inject注入實例,可見他也是先調makeCar()得到實例,然後調DaggerActivity_MembersInjector.injectMCar進行注入,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//DaggerActivity_MembersInjector.java\[email protected](\"com.holiday.srccodestudy.dagger.DaggerActivity.mCar\")\npublic static void injectMCar(DaggerActivity instance, Car mCar) {\n //引用DaggerActivity的成員,爲其賦值,可見mCar不能聲明爲private\n instance.mCar = mCar;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3. 新的造車,從Provider獲取"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在造新車NewCar時,是從Provider獲取的,跟進newCarProvider.get(),如果使用了單例@Singleton,NewCar"},{"type":"text","marks":[{"type":"italic"}],"text":"Factory會被DoubleCheck包一層,DoubleCheck會加鎖和處理單例邏輯,我們直接看NewCar"},{"type":"text","text":"Factory的get就行了,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//Factory extends Provider\nclass NewCar_Factory implements Factory {\n private final Provider> engineMapProvider;\n\n public NewCar_Factory(Provider> engineMapProvider) {\n this.engineMapProvider = engineMapProvider;\n }\n\n @Override\n public NewCar get() {\n //通過engineMapProvider獲取多引擎map\n return newInstance(engineMapProvider.get());\n }\n\n public static NewCar_Factory create(Provider> engineMapProvider) {\n return new NewCar_Factory(engineMapProvider);\n }\n\n public static NewCar newInstance(Map engineMap) {\n //new出新車實例\n return new NewCar(engineMap);\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看下多引擎map又是如何獲取的,engineMapProvider.get(),"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//MapFactory.java\n//MapFactory extends AbstractMapFactory implements Factory\npublic Map get() {\n //創建新的LinkedHashMap\n Map result = newLinkedHashMapWithExpectedSize(contributingMap().size());\n //遍歷已存入的引擎,存進新map\n for (Entry> entry : contributingMap().entrySet()) {\n result.put(entry.getKey(), entry.getValue().get());\n }\n //返回不可修改的map\n return unmodifiableMap(result);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"至此,dagger的各生產線流程就分析完了,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2d/2d70f48de67b9f4fb08089af8a6d3279.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"細枝"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"@Scope作用域、@Subcomponent子組件、還有SPI(Service Provider Interface)、grpc(谷歌的遠程過程調用框架)等,都留到細枝篇來寫啦。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"使用場景"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那dagger在Android中有哪些用武之地?首先是從"},{"type":"codeinline","content":[{"type":"text","text":"架構角度"}]},{"type":"text","text":",在"},{"type":"link","attrs":{"href":"https://developer.android.google.cn/training/dependency-injection/dagger-android","title":""},"content":[{"type":"text","text":"谷歌示例"}]},{"type":"text","text":"中,結合了Activity、僞ViewModel、Repository、DataSource和Retrofit來使用dagger,(還沒用過,不知道香不香、坑多不多,靠屏幕前的大佬們反饋了~)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a9/a9e5e0e18bed29a5688e34a1905ebe4a.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後我們在項目中的一些場景,是從"},{"type":"codeinline","content":[{"type":"text","text":"業務角度"}]},{"type":"text","text":"切入,在個別複雜度較高的業務線上單獨使用dagger。例如錢包業務,有"},{"type":"codeinline","content":[{"type":"text","text":"大量實例"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"大量頁面/視圖"}]},{"type":"text","text":"存在多對多關係,比如錢包Act需要錢包Api、錢包用戶信息Manager;充值Act需要支付Api、充值Service;銀行卡列表View需要銀行Service...像這種多對多、對象依賴關係雜亂無章的場景,很適合用dagger來幫我們注入。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@PurseScope //錢包作用域\[email protected](modules = PurseModule.class) //由PurseModule提供實例\npublic interface PurseComponent {\n\n //爲act注入\n void inject(退款詳情Act xxxAct);\n void inject(銀行卡首頁Act xxxAct);\n void inject(錢包首頁Act xxxAct);\n void inject(錢包設置Act xxxAct);\n void inject(餘額首頁Act xxxAct);\n //...\n\n //爲view注入\n void inject(錢包首頁GridContainer xxxGridContainer);\n void inject(銀行卡列表View xxxView);\n\n //提供網絡調用\n 支付Api xxxApi();\n\n //提供錢包用戶信息\n 錢包用戶信息Manager xxxManager();\n\n //...\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後看到PurseModule,用於提供各種實例,可以new、手動單例、工廠生產..."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Module\npublic class PurseModule {\n @Provides\n @PurseScope\n public 支付Api provide支付Api(...) {\n return new 支付Api(...);\n }\n\n @Provides\n @PurseScope\n public 用戶信息Manager provide用戶信息Manager(...) {\n return 用戶信息Manager.get(...);\n }\n\n @Provides\n @PurseScope\n public 充值Service provide充值Service(...) {\n return new 充值ServiceFactory.create(...);\n }\n\n //...\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"尾聲"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"簡單總結下dagger的優缺點~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"優勢:無反射、支持增量編譯"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"缺點:構建過程需要而外的io和編譯時間、生成類會增大包體積、不夠好用、"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"後續計劃:dagger細枝、hilt、koin、順便看看spring上的注入@Autowired是怎麼做的...學海無涯啊~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/19/19c2bdd50012063aabee855cf06c04d3.jpeg","alt":null,"title":"","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"系列文章:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://juejin.im/post/6862368973888061453","title":""},"content":[{"type":"text","text":"《看完不忘系列》之Retrofit"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://juejin.im/post/6856966817844625415","title":""},"content":[{"type":"text","text":"《看完不忘系列》之okhttp"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://juejin.im/post/6850037281349173261","title":""},"content":[{"type":"text","text":"《看完不忘系列》之Glide"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"參考資料"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://github.com/google/dagger","title":""},"content":[{"type":"text","text":"GitHub"}]},{"type":"text","text":" & "},{"type":"link","attrs":{"href":"https://dagger.dev/","title":""},"content":[{"type":"text","text":"文檔"}]},{"type":"text","text":" & "},{"type":"link","attrs":{"href":"https://dagger.dev/api/latest/","title":""},"content":[{"type":"text","text":"API"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://developer.android.google.cn/training/dependency-injection","title":""},"content":[{"type":"text","text":"谷歌 - Android 中的依賴項注入"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/rizpnU3jgm_aZ2-8m9zp6Q","title":""},"content":[{"type":"text","text":"谷歌 - 在 Kotlin 中使用 Dagger 會遇到的陷阱和優化方法"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://juejin.im/post/6854828407960633352","title":""},"content":[{"type":"text","text":"掘金 - 從Dagger到Hilt,谷歌爲何執着於讓我們用依賴注入"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://tva1.sinaimg.cn/large/007S8ZIlly1ghzod486q7j30b40b4wen.jpg","title":""},"content":[{"type":"text","text":"歡迎關注原創技術公衆號:哈利迪ei"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章