理解 Dagger 2 应用架构

understanding Dagger 2 app Architecture

我正在努力了解 Dagger 2 和依赖注入。我认为一个好的方法是看一下官方 Coffee example。我也通读了github页面上的官方文档,但我发现它对新手来说相当混乱。

正如您在下图中看到的,我剥离了所有 类 并为它们着色以了解发生了什么。但是我还是有些疑惑

我的问题:

1) 我听说我们使用 DI,因为在构造函数上传递依赖项会使我们的代码冗长。好吧,这个例子中的代码量和 类 大大超过了在构造函数中仅提供这两个参数所需要的量。此外,代码将更加人性化。这样做的好处是什么?

2) PumpModule 声明了一个提供者,它将它应该提供的东西作为参数……这有点违反直觉。那里到底发生了什么?

3)我真的迷路了(CoffeApp.java)

DaggerCoffeApp_Coffe.builder().build()

那是干什么的? DaggerCoffeApp_Coffe 在哪里? Android 工作室表示无法在任何地方找到它。

回答 1

"the amount of code and classes on this example enormously exceeds what it would have taken to just supply these two parameters in a constructor"

在示例代码或非常小的应用程序中 - 是的。在一个正常大小或大的应用程序中,如果不是数千次,你将有数百次提供 "these two parameters in a constructor".

DI 的最大优点之一是它允许(并且在某种程度上可能强制)您创建模块化应用程序,可以在其中开发模块并 测试隔离。这听起来可能没什么大不了的,但同样,当应用程序变得更大时,在不破坏东西的情况下添加新的更改变得非常困难。开发模块时,您可以通过定义提供所需功能的接口并为这些接口定义 @Inject 来将自己与应用程序的其余部分隔离开来。这样,如果您稍后(几个月,下一个版本?)决定需要 change/extend/rewrite 一些模块,只要您不更改它的接口,其他模块就不会受到影响。您将能够编写您的替换模块,然后在您的 @Provides 方法中只 'switch' 到它。

DI 的另一大优势是它允许您轻松地将模拟对象提供给单元测试。例如:假设您有一个 Activity,它使用 GPS 位置提供程序来检测位置。如果你想在没有 DI 的情况下测试它,你必须在调试模式下 运行 你的应用程序在模拟器中,手动提供一些 "fake" 位置并在某些断点检查 activity 是否在预期状态。使用 DI,您可以轻松地将模拟位置提供程序提供给您的 Activity,它使用您预定义的一些值模拟 GPS 位置更新。当然,您可以再次 运行 在模拟器(或真实设备)中手动 运行 您的应用程序,但您也可以 运行 它作为单元测试的一部分自动 运行 甚至在像 Jenkins 这样的持续集成服务器进程中。这样,每次更改代码时,您都可以 运行 测试并立即查看更改是否破坏了某些内容。另一个价值是自动测试可以节省 您的 时间。在示例中,您可能需要至少 2 分钟的时间来进行手动测试。自动测试将花费几秒钟,更重要的是它将 运行 不需要你的 attention/input 而 运行ning.

有关更多信息,我推荐 Jake Wharton 的这段精彩视频: https://www.parleys.com/tutorial/5471cdd1e4b065ebcfa1d557/

这里是视频的幻灯片: https://speakerdeck.com/jakewharton/dependency-injection-with-dagger-2-devoxx-2014

回答2

"PumpModule declares a provider that takes the thing it is supposed to provide as a parameter"

该提供商提供 接口 ,而不是具体的 class。这就是重点。当您将应用程序迁移到 DI 时,您必须为每个要注入的 class 创建一个接口。正如答案 1 中所解释的那样,您将能够轻松地将具体实现替换为用于测试的模拟对象,或者为更高版本的应用程序替换为新的更好的实现。 例如:在某些时候,您决定需要 Rotary Vane Pump 而不是 Thermosiphon。您编写 RotaryVanePump class 然后只需将您的方法更改为 @Provides Pump providePump(RotaryVanePump pump) {.

这是如何工作的((过度)简化解释):

  1. DI图由DaggerCoffeApp_Coffe.builder().build()构建(请先看答案3)
  2. 在某些时候 Dagger 在您的代码中发现 @Inject Pump mMyPump;
  3. Dagger 发现您需要注入 Pump 并在 DI 图中寻找如何提供它。
  4. 它找到了 @Provides Pump providePump() 方法。 Dagger 发现它需要 RotaryVanePump 个对象。
  5. Dagger寻找DI图如何提供​​RotaryVanePump.
  6. 任何模块中都没有 provide 方法,但 RotaryVanePump 不需要,因为它具有无参数构造函数,因此 Dagger 可以实例化一个对象。
  7. 新对象被实例化为 RotaryVanePump
  8. 类型
  9. Dagger 在 providePump() 中将此对象作为实际参数提供。
  10. providePump() returns 将该对象作为 return 值。
  11. RotaryVanePump被注入到@Inject Pump mMyPump字段。

而且这一切都是自动完成的,你不必关心它。

回答 3

DaggerCoffeApp_Coffe 由 Dagger 生成。您必须使用它才能 Android studio "see" 生成的 classes。 https://bitbucket.org/hvisser/android-apt

"What is that doing?

这就是整个魔术 :-)。它构建依赖关系图并在编译时检查是否满足所有依赖关系。 "at compile time" 将 Dagger 与所有其他无法在编译时验证图形的 DI 框架区分开来,如果您错过定义某些依赖项,您将得到一个丑陋的 运行 时间错误。

为了让您的所有 @Inject 正常工作,您必须首先使用这样的调用构建图表。