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\n@Module\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\n@Singleton //造車圖紙也需跟着單例\n@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":"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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.