为什么我们需要在 Android 多模块化架构中将 DI 布线与实现分开?

Why do we need to separate DI wiring from Implementation in Android multi modular architecture?

Square Inc. 在 Droidcon SF'19 上展示了它的内部模块化架构:

https://www.droidcon.com/media-detail?video=380843878

但是,我对一些项目符号有点困惑。你能帮帮我吗?

  1. 为什么他们实际上需要 :wiring 模块?我发现它增加了复杂性:

那么为什么要 :wiring 个模块呢?

可以将 :impl:impl-wiring:fake:fake-wiring 合并在一起,以消除上述所有问题。而且,在 :demo-apps 中,可以只依赖于 :impl:fake``, and not on :impl-wiring(or:fake-wiring```).

创建此类模块是为了分离更多。有了这个,你就可以生成你使用的组件类型(koin、dagger)以及如何使用的抽象。如果项目很大,那么做是有意义的。


目前我在模块之间生成以下依赖流:

警告:仔细检查方向性。

  • :feature-A:open <- :feature-A:impl -> :feature-A:impl-wiring
  • :feature-A:impl-wiring -> :feature-A:impl, :feature-A:open
  • :app -> :feature-A:open, :feature-A:impl-wiring

我仍然不确定应用是否应该依赖于 open impl-wiring,或者哪个应用应该只依赖于 [=17] 的 openopen =].

最终,我想到了以下解决方案:

每个功能都包含以下 gradle 个模块:

api

implfake

data:api

data:impl1 ... data:implNdata:fake

data:wiring

ui

demo

所以,这里 apiimplfake 和往常一样,但我将数据层分开了。我自己认为有时我需要多个不同的数据层实现,例如 - 如果我开发 Stock-Charts 应用程序,我可以依赖 Finnhub Open API or MBOUM API 或提供假实现。

因此我有data:apidata:implX。事实上,data:api 定义了 FeatureRepository 接口(一个或多个)并且 data:implX 为它们提供了实际的实现。为了绑定接口和实现,我使用 data:wiring,它定义了 Dagger modulescomponent(s)。此外,我在每个 data:implX 模块中保留相同的包名称,以便 “只写一次” data:wiring 模块。为了用另一个实现替换一个实现,我只更改 data:wiring/build.gradle 中的一行,其中说明了一种:

implementation project(":data:implA")implementation project(":data:implB")

此外,为了打破我原来问题中提到的混淆,我引入了 ui 模块,其中包含一些 Views 的特定功能。 Fragments 进入 demo(用于测试功能的独立应用程序)或 ui,它们指的是 viewModel,其中有一些绑定 ctor-injected from一个特征的 Dagger component。但是 UI 和库在这里是分开的。 Fragment 实例化一个专用的 Dagger component,它使用 component dependencies 来引用功能的库绑定,例如 interactorrepository

因此,总结一下 - 每个 功能 的 UI 和业务逻辑实现(“库”)之间的分离使得解决问题成为可能。功能的 api 将其功能声明为库的入口点,并且可以通过 :app 的 Dagger multibindings 进行全局访问。所以它可以在任何 :demo:ui:dynamic-feature.

中进一步使用