Choosing a dependency framework for your Android app is a big decision, it’s not something that you can easily replace later on. Dependency injection spans all layers of your app so it’s important that the DI framework that you choose meets all your requirements and that includes testing.
为您的Android应用程序选择依赖关系框架是一个重大决定,这不是您以后可以轻松替换的事情。 依赖注入覆盖了应用程序的所有层,因此,选择的DI框架必须满足所有要求,包括测试,这一点很重要。
![Image for post](https://miro.medium.com/max/9999/1*pDnFQZzm8zd8ESh5eJfZ9A.png)
I am going to compare 4 popular frameworks: Dagger, Koin, Kodein, and Toothpick. It’s interesting that all of these frameworks also run on the JVM and 2 of the frameworks also integrate with the popular Ktor framework. I am going to demonstrate how to integrate these DI frameworks into a simple application. Although the application will run on Android many of these concepts can be easily applied to non-Android projects.
我要比较4个流行的框架: 匕首 , 孝允 , Kodein和牙签 。 有趣的是,所有这些框架也都在JVM上运行,并且其中两个框架还与流行的Ktor框架集成。 我将演示如何将这些DI框架集成到一个简单的应用程序中。 尽管该应用程序将在Android上运行,但其中许多概念仍可以轻松应用于非Android项目。
The application contains a GreetingService
which may either return “Good morning” or “Good evening” depending on the hour of the day.
该应用程序包含GreetingService
,该服务可能会根据一天中的小时返回“早安”或“晚安”。
The TimeService
is used to return the current hour of the day. The GreetingService
will use the TimeService
to determine if it is morning or night and tailor the message accordingly.
TimeService
用于返回当天的当前时间。 GreetingService
将使用TimeService
确定是上午还是晚上,并相应地调整消息。
The MessageData
is used in the greeting message to add additional text to the message.
在问候消息中使用MessageData
将其他文本添加到消息中。
As you can see it’s a pretty simple example. I’ll show you how to use each DI framework and more importantly how to test your code. Even if your a non-Android developer you should still be able to understand these examples. Basically, we initialize our DI container when our application starts up using the Application
class and we display our greeting message on the screen in the Activity
class. That’s pretty much all Android knowledge you need.
如您所见,这是一个非常简单的示例。 我将向您展示如何使用每个DI框架,更重要的是向您展示如何测试您的代码。 即使您不是Android开发人员,您仍然应该能够理解这些示例。 基本上,我们在使用Application
类启动应用程序时初始化DI容器,并在Activity
类的屏幕上显示问候消息。 这几乎就是您需要的所有Android知识。
框架比较 (Framework Comparison)
Before we get started here is a high-level overview of each framework showing the popularity, who supports its and where it runs. Later we’ll dive deeper into how each framework is used and see what the code looks like. As you can see Dagger is by far the most popular framework with 14.8K stars on Github.
在我们开始之前,这里是每个框架的简要概述,显示了流行程度,支持者以及其运行位置。 稍后,我们将深入研究如何使用每个框架,并查看代码的外观。 如您所见,Dagger是迄今为止最受欢迎的框架,在Github上具有14.8K个星标。
![Image for post](https://miro.medium.com/max/9999/1*Ly2bct4DGfIfPXZqRqNhWw.png)
Now, let's get started!
现在,让我们开始吧!
科恩 (Koin)
Koin was designed to be a pragmatic lightweight dependency injection framework that doesn’t use code generation, proxies or reflection. Over the last couple of years it has become very popular due to its simplicity.
Koin被设计为一个实用的轻量级依赖注入框架,不使用代码生成,代理或反射。 在过去的几年中,由于其简单性,它已变得非常流行。
Koin代码 (Koin code)
Koin uses the Module
to wire up its dependencies.
Koin使用Module
连接其依赖项。
Here the DI container is initialized with the Module
declared above.
在这里,DI容器使用上面声明的Module
初始化。
To display the greeting on the screen we need to inject the GreetingService
and call the service after it is injected.
为了在屏幕上显示问候语,我们需要注入GreetingService
并在注入后调用该服务。
Koin测试 (Koin testing)
Koin provides its own mock classes that making testing the GreetingService
really easy so you don’t need another mocking framework.
Koin提供了自己的模拟类,这使得对GreetingService
测试非常容易,因此您不需要其他模拟框架。
Performance metrics from the Koin website.
Koin网站上的效果指标。
![Image for post](https://miro.medium.com/max/9999/1*6xr83KmNyeitWC9vkSbMsA.png)
Overall I found it really easy to setup Koin, it has a very nice DSL, good support for Android and I had no trouble with testing the injection.
总的来说,我发现设置Koin真的很容易,它具有非常好的DSL,对Android的良好支持,并且在测试注入时没有问题。
好 (Good)
- Good documentation 好的文档
- Easy mocking for testing 轻松模拟测试
- Simple & clean DSL 简单干净的DSL
- Supports scopes 支持范围
- Android & JVM support Android和JVM支持
- 2nd most popular framework in Android Android中第二受欢迎的框架
- Ktor support Ktor支持
坏 (Bad)
- Kotlin Multi-Platform support Kotlin多平台支持
- get() in module 模块中的get()
- Slower than the DI frameworks that generate code 比生成代码的DI框架慢
- Doesn’t support JSR 330 不支持JSR 330
科丁 (Kodein)
The Kodein framework was designed to provide painless dependency injection in Kotlin projects. It’s one of the first pure Kotlin dependency libraries and supports multiple platforms including iOS, Javascript, Android, and the JVM.
Kodein框架旨在在Kotlin项目中提供无痛的依赖注入。 它是最早的纯Kotlin依赖库之一,并支持包括iOS,Javascript,Android和JVM在内的多个平台。
科丁编码 (Kodein code)
Wiring up the dependencies is pretty similar to Koin. In the Module
you bind the interfaces to the implementations.
连接依赖项与Koin非常相似。 在Module
,将接口绑定到实现。
The DI container is also initialized with the Module
and started in the Application
. Kodein uses a interface calledKodeinAware
to mark the classes that are used for dependency injection.
DI容器也使用Module
初始化,并在Application
启动。 Kodein使用称为KodeinAware
的接口来标记用于依赖项注入的类。
The Activity
also needs to use the KodeinAware
interface so the GreetingService
can be injected.
该Activity
还需要使用KodeinAware
接口,以便可以插入GreetingService
。
科丁测试 (Kodein testing)
Kodein provides a nice dependency override feature so you can easily inject fakes into the GreetingService.
Kodein提供了很好的依赖项覆盖功能,因此您可以轻松地将伪造的东西注入GreetingService.
It took me a little bit longer to get the Kodein example up and running mainly due to the documentation of the website being out of date. The DSL is similar to Koin but not as clean. I also found it to be a little bit more complicated than Koin.
我花了一点时间来建立和运行Kodein示例,主要是因为该网站的文档已过时。 DSL与Koin相似,但不干净。 我还发现它比Koin复杂一点。
好 (Good)
- Supports Kotlin Multiplatform 支持Kotlin多平台
- Easy to test with fakes 易于测试假货
- Ktor support Ktor支持
- Good Android support 良好的Android支持
- Simpler than Dagger 比匕首简单
坏 (Bad)
- Documentation not up to date 文档不是最新的
- instance() in module 模块中的instance()
- DSL and syntax not as clean as Koin & Toothpick DSL和语法不如Koin&Toothpick干净
- More complex than Koin & Toothpick 比Koin&牙签更复杂
- Slowest of all 4 frameworks 所有4个框架中最慢的
- JSR 330 support JSR 330支持
匕首 (Dagger)
Dagger is a fully optimized dependency framework for Android using generated code to avoid reflection. It is maintained by Google and is their recommended solution for doing DI on Android.
Dagger是针对Android的完全优化的依赖项框架,使用生成的代码来避免反射。 它由Google维护,是他们在Android上进行DI的推荐解决方案。
匕首代码 (Dagger code)
A Dagger Module
is a little bit more verbose than the Modules
used in the other frameworks. It uses annotations to describe the classes and wire up the dependencies as follows;
Dagger Module
比其他框架中使用的Modules
更加冗长。 它使用注释来描述类,并按如下所示连接依赖项;
The constructor of theGreetingServiceImpl
also needs to modified to add the @Inject annotation for Dagger to declare it as a Singleton.
This also needs to be done for the TimeServiceImpl.
Note that you use also need to remove the default message for the MessageData
class otherwise will complain about multiple constructors.
GreetingServiceImpl
的构造函数还需要修改以为Dagger添加@Inject批注,以将其声明为Singleton.
对于TimeServiceImpl.
也需要完成此操作TimeServiceImpl.
请注意,您还需要删除MessageData
类的默认消息,否则会抱怨多个构造函数。
Dagger requires an additional Component
class to specify the Modules
that will be used in your app. You also need to define the methods that will inject your classes and a Factory
to inject the String
into the constructor of the MessageData
class.
Dagger需要附加的Component
类来指定将在您的应用程序中使用的Modules
。 您还需要定义将注入类的方法,以及定义将String
注入MessageData
类的构造函数的Factory
。
The generated class DaggerComponent
is used to start the DI container and is initialized with the Module
.
生成的类DaggerComponent
用于启动DI容器,并使用Module
初始化。
To display the greeting on the screen you need to retrieve the Dagger Component
from the Application
and inject the Activity
with theGreetingService
.
要在屏幕上显示问候语,您需要从Application
检索Dagger Component
,并使用GreetingService
注入Activity
。
匕首测试 (Dagger testing)
I checked the Dagger codelab and the recommendation way to test your classes was with mocks without using Dagger. That’s one of the reasons why Google recommends constructor injection over field injection.
我检查了Dagger代码实验室,推荐的测试类的方法是使用模拟而不使用Dagger。 这就是Google建议使用构造函数注入而不是字段注入的原因之一。
Dagger still requires quite a lot of boilerplate to wire up your dependencies and use it in your app. It will be a welcome relief when Google addresses these issues and simplifies the syntax.(Update Aug 20 : Hilt has been released to address these issues) If you work as a consultant and work on many different apps most likely you will run into Dagger eventually, so it’s one of those frameworks that you have to know anyway.
Dagger仍然需要大量样板来连接您的依赖项并在您的应用程序中使用它。 当Google解决这些问题并简化语法时,这将是令人欣慰的放松。(8月20日更新:已发布Hilt以解决这些问题)如果您作为顾问并从事许多不同的应用程序,则很可能最终会遇到Dagger ,因此它还是您必须要了解的那些框架之一。
好 (Good)
- JSR 330 support JSR 330支持
- Feature-rich 功能丰富
- Good documentation & codelab 好的文档和代码实验室
- Lots of examples and blog articles 很多例子和博客文章
- Most popular DI on Android Android上最受欢迎的DI
- Google support 谷歌支持
- Compile-time checking of dependencies 依赖项的编译时检查
- Fast 快速
- Google working to addressing the bad Google致力于解决不良问题
- Uses incremental annotation processing 使用增量注释处理
坏 (Bad)
- Overly complex 过于复杂
- Need to reference generated classes 需要引用生成的类
- Boilerplate 样板
- Can be slow working with your IDE 使用IDE可能会很慢
- No DSL 没有DSL
- Kotlin Multi-Platform support Kotlin多平台支持
牙签 (Toothpick)
Toothpick is a scope tree based Dependency Injection (DI) library for Java/Kotlin/Android and maintained by Groupon. It is a full-featured, runtime based, but reflection-free, implementation of JSR 330.
Toothpick是用于Java / Kotlin / Android的基于作用域树的依赖注入(DI)库,由Groupon维护。 它是功能全面的,基于运行时的,但无反射的JSR 330实现。
牙签代码 (Toothpick code)
Toothpick’s DSL is the easiest to read, it also generates factories for instance creation avoiding the need for the ugly method calls such as get() or instance().
牙签的DSL最容易阅读,它还会生成工厂用于实例创建,从而避免了使用诸如get()或instance()之类的丑陋方法的需求。
For Toothpick to generate these factories you need to add the @InjectConstructor annotation to the class being injected. Alternatively, you can also use the @Inject annotation constructor syntax which was used in the Dagger example.
为了使Toothpick生成这些工厂,您需要将@InjectConstructor批注添加到要注入的类中。 另外,您也可以使用Dagger示例中使用的@Inject注释构造函数语法。
The Module
is installed in the root scope when the application starts up.
应用程序启动时, Module
将安装在根作用域中。
The GreetingService
is injected into the Activity
using the root scope. Once injected the GreetingService
can be used to display the greeting on the screen.
使用根范围将GreetingService
注入到Activity
。 注入后, GreetingService
可以用于在屏幕上显示问候语。
牙签测试 (Toothpick testing)
The ToothPickRule class allows you to inject the class that you are testing with mocks.
ToothPickRule类允许您注入要通过模拟测试的类。
You can also override classes with fakes by installing a test Module
.
您还可以通过安装测试Module
用伪造品覆盖类。
Just for completeness, this how you would use Toothpick and Roboelectric.
仅出于完整性考虑,这就是您使用牙签和Roboelectric的方式。
Performance metrics from the Toothpick website.
来自Toothpick网站的性能指标。
On an average of 50 runs, the cost of setting up the injection system, i.e creating an injector (RG), a component (Daggers), and a scope (Toothpick) are:
平均运行50次,设置注射系统(即创建注射器(RG),组件(匕首)和示波器(牙签))的成本为:
- RoboGuice 4: 105 ms RoboGuice 4:105毫秒
- Dagger 1: 20 ms 匕首1:20毫秒
- Dagger 2: 22 ms 匕首2:22毫秒
- Toothpick: 1 ms 牙签:1毫秒
![Image for post](https://miro.medium.com/max/9999/1*DXg78i7oTgzmaRKz-odSPQ.png)
Toothpick’s clean DSL and clear use of scopes makes it easy to understand and use Toothpick on your project. It’s the fastest DI framework around and one of the easiest to test. Although it uses generated classes you don’t have to reference any of these classes.
牙签的干净DSL和明确的范围使用使您在项目中易于理解和使用牙签。 它是最快的DI框架,也是最容易测试的框架之一。 尽管它使用生成的类,但您不必引用任何这些类。
好 (Good)
- JSR 330 support JSR 330支持
- Supports both annotations & delegates 同时支持注释和委托
- Very good documentation 很好的文档
- Supported by Groupon 由Groupon支持
- Cleanest DSL compared to Koin & Kodein 与Koin和Kodein相比最干净的DSL
- Easy to test 易于测试
- Fastest of the 4 frameworks 4个框架中最快的
- Uses incremental annotation processing 使用增量注释处理
坏 (Bad)
- Kotlin Multi-Platform support Kotlin多平台支持
- Not many examples & blogs posts about it 关于它的示例和博客文章并不多
最终排名 (Final standings)
When I evaluate frameworks usability and testability are always at the top of my list. A framework doesn’t have to be the fastest or be written entirely in Kotlin, as long as the framework provides nice Kotlin extensions and runs at a reasonable speed that is good enough for me. Knowing that the framework is well supported and maintained is also another consideration. Having good documentation is also another must and that’s why some frameworks such as Spring have dominated their domains.
在评估框架时,可用性和可测试性始终是我的首要任务。 只要框架提供了不错的Kotlin扩展并且可以以对我足够的合理速度运行,则框架不必是最快的或完全用Kotlin编写的。 知道框架得到良好的支持和维护也是另一个考虑因素。 拥有良好的文档编制也是另一个必须的,这就是为什么诸如Spring之类的某些框架在其领域中占主导地位的原因。
Taking this all into consideration, the winner of this shoot out is Toothpick followed closely by Koin. I feel it would be quite easy for the contributors of Kodein to address the issues I had and also update their documentation. I put Dagger last because in its current form it’s just too complex and verbose to use. It’s good to know that Google is committed to addressing these issues and improving the developer experience. Hopefully in the future it can improve its standing and bring bigger guns to the next shoot out!
考虑到所有这些因素,这次枪战的赢家是牙签,紧随其后的是科恩。 我觉得Kodein的撰稿人很容易解决我遇到的问题并更新他们的文档。 我把Dagger放在最后是因为它的当前形式太复杂且难以使用。 很高兴知道Google致力于解决这些问题并改善开发人员的体验。 希望在将来它可以改善自己的位置并为下一次射击带来更大的枪支!
1st Toothphick — The winner of this shootout.
1st Toothphick (第一个牙签) -这场枪战的赢家。
2nd Koin — A close second.
第二届孝允 -紧随其后。
3rd Kodein — A decent contender
第三科丁 -一个体面的竞争者
4th Dagger — Not the fastest shooter out there, it needs fewer bullets not more.
第4 匕首 -并不是最快的射手,它需要的子弹更少而不是更多。
The working code for all the examples including the tests can be found at this repo.
在此仓库中可以找到所有示例(包括测试)的工作代码。
Kotlin多平台和DI (Kotlin Multiplatform & DI)
I am really excited about KMP (Kotlin Multiplatform) and hope it lives up to its promises, although it’s still experimental many companies are now using it in production. I have been wondering for a while about how or if DI will be used in a KMP app. I know that many iOS apps don’t use DI and that's why I am wondering if the shared code would still it. At present only Kodein supports KMP and according to Kevin Galligan there is also a forked version of Koin that will support KMP in the future. My prediction is that KMP will disrupt the way we write apps and also the libraries we use, we just have to wait a little bit longer for that to happen.
我对KMP(科林多平台)感到非常兴奋,并希望它能兑现其诺言,尽管它仍处于试验阶段,许多公司现在都在生产中使用它。 我一直想知道在KMP应用程序中如何或是否使用DI。 我知道许多iOS应用程序不使用DI,这就是为什么我想知道共享代码是否还会使用DI。 目前,只有Kodein支持KMP,据Kevin Galligan 所说 , Koin还有一个分支版本,它将在将来支持KMP。 我的预测是,KMP将破坏我们编写应用程序以及使用的库的方式,我们只需要稍等一会儿就可以实现。
Thanks to Karl Maul for editing.
感谢Karl Maul进行编辑。
Btw, we use both Toothpick and Dagger in our production apps. If you liked this article about dependency injection also check this one out.
顺便说一句,我们在生产应用程序中同时使用了Toothpick和Dagger。 如果您喜欢这篇关于依赖注入的文章,也请查看这篇文章。
翻译自: https://proandroiddev.com/a-dependency-injection-showdown-213339c76515