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> 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\n@InjectedFieldSignature(\"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 //钱包作用域\n@Component(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}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章