考虑Android项目模块化
已发表: 2019-08-21当一个项目达到一定规模时,在单个模块中进一步工作就会变得不那么有效。 然后将其模块化成为一种有效的解决方案。
模块化的4大优势
在决定模块化之前,最好明确所涉及的内容。 模块化 android 项目结构的优点包括:
更好的代码隔离
每个模块都可以公开其公共 API 接口并隐藏实现细节。 对于单个模块,您不能完全确定它的实现是否被很好地隐藏(尤其是在 Kotlin 中,其中包可见性修饰符不可用)。
更容易的新技术评估
创建模块时,您可以在不影响其他模块的情况下检查新的架构模式或新库。
减少项目构建时间
修改一个模块需要重建该模块和其他依赖它的模块。 通过阅读本文档了解其工作原理:Android 开发人员。 依赖配置
工作更方便
更容易分析/调试/重构更小的和孤立的代码片段。 此外,新开发人员的入职速度会更快。
这些好处听起来足以说服您开始模块化过程,但是您如何开始呢?
#1:确定您的模块及其关系
有两种方法可以识别您的模块:按功能和按层。
在功能模块下,您可以了解用户可用的应用程序的某些区域(例如登录、仪表板、个人资料等)。 这些区域可以由单个屏幕或涵盖某些流程的屏幕流组成。 这种类型的模块不能依赖于相同类型的模块。
确定特征后,您肯定需要提取一些甚至所有模块所需的通用功能。 这些模块可以负责单独的架构层(如持久存储、网络、导航、UI 组件……)或处理一组功能使用的某些数据的业务逻辑。 这些类型的模块通常称为库。 库模块可以构建依赖树。
除了功能和库模块之外,还需要一个模块来管理其他模块之间的水平连接(下一点对此进行更多介绍)。 该模块将包含一个自定义应用程序类和一个依赖注入设置。 没有其他模块可以依赖于这个模块,但是这个模块依赖于项目中的所有其他模块。

考虑到上述定义,模块层次结构可能如下所示:
#2:依赖注入设置
尽管项目模块之间存在依赖关系,但您还应该设置 dagger 依赖关系。 Dagger 提供了两种声明依赖的方式:子组件和组件依赖。
Dagger 子组件依赖要求父组件声明所有依赖的子组件。 在项目模块之间,这种关系是行不通的,因为它颠倒了项目模块依赖的方向。 但它可以在单独的项目模块中使用。
Dagger 组件依赖更加灵活,因为子组件可以声明它依赖于父组件。 这使得在单独的项目模块之间使用这种依赖关系成为可能。
在某些时候,您可能会发现一个模块需要的关于另一个模块的知识有限。 一个很好的例子是功能模块之间的导航。 提供这种关系通常称为水平依赖。 要在单独的模块之间创建此通信通道,需要具有接口的附加模块来描述此通信和将实现绑定到声明的接口的模块。
用于管理水平依赖的项目模块依赖设置如下图所示:

本文末尾的项目中提供了此类关系的示例代码。
#3:Gradle 设置
每个项目模块都有其 gradle.build,除了应用了添加的依赖项和插件外,几乎相同。 因此,最好将重复配置提取到项目目录根目录下的一个 gradle 文件中。 这样的文件还可以注册常见的 gradle 任务来执行静态代码分析或运行单元测试。

此类常见设置的代码片段可在此处找到:
afterEvaluate { project -> def isAndroid = project.plugins.hasPlugin('com.android.library') || project.plugins.hasPlugin('com.android.application') setupModule(isAndroid) setupCommonTestDependencies(isAndroid) setupCommonTasks(isAndroid) } def setupModule(isAndroid) { if (isAndroid) { android { compileSdkVersion projectCompileSdk defaultConfig { minSdkVersion projectMinSdk targetSdkVersion projectTargetSdk } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } lintOptions { abortOnError true checkReleaseBuilds false checkAllWarnings true warningsAsErrors true def lintBaseline = file("quality/lint-baseline.xml") if (lintBaseline.exists()) baseline lintBaseline } } } else { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } } def setupCommonTestDependencies(isAndroid) { dependencies { testImplementation "junit:junit:${junitVersion}" testImplementation "org.assertj:assertj-core:${assertJVersion}" testImplementation "org.mockito:mockito-core:${mockitoVersion}" testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:${mockitoKotlinVersion}" if (isAndroid) { androidTestImplementation "androidx.test.ext:junit:${axTestJUnitVersion}" androidTestImplementation "androidx.test.espresso:espresso-core:${axEspressoLibVersion}" } } } def setupCommonTasks(isAndroid) { if (isAndroid) { tasks.register("unitTest") { task -> task.dependsOn(testDebugUnitTest) } } else { tasks.register("unitTest") { task -> task.dependsOn(test) } } }
结论
本文并非详尽无遗,也不是模块化 Android 项目的完整指南。 但我认为它解决了在开始项目模块化时应该考虑的方面。
在链接中可以找到一段展示水平依赖的代码。
想要为 Android 构建原生应用程序? 选择米奇多!