在多个应用程序中重复使用Gradle模块
#android #体系结构 #gradle #git

在多个应用程序中重复使用Gradle模块

大家好!我已经担任Android开发人员已经有一段时间了,我不得不说:与十年前相比,今天的应用程序具有更清晰的架构,并且是模块化的。事实上,多模块化(或至少在项目中的几个Gradle模块)实际上是现代标准。通常,任何项目都具有主应用模块,几个功能模块以及 core/域/数据模块。

https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xqwe81qbuhtewccjxm3y.png

这些模块以非常标准的方式连接:

https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v7rn8o6z9szf023fuely.jpg

当您拥有一个应用程序时,这不是一个问题:所有模块共存,我们不在乎模块之间的连接,但是,我们当然应该避免使用 councular依赖关系

事实上,我们肯定可以在一个项目中拥有多个应用程序。为此,我们只需要添加另一个:app gradle模块,在其中声明android.application插件,然后添加ta da! Android Studio显示了另一个启动选项。

例如,我们正在研究两个应用程序: catsapp ,用于猫爱好者,另一个是 dogpapp ,用于狗狗。所有功能都是相同的(我们有一个屏幕来显示猫和狗列表以及屏幕以获取详细信息);只有主模块不同。除其他事项外,可以声明不同的网络配置和themes.xml文件,并且在这些模块中以不同的方式进行DI树的初始化。您可以找到这样一个项目here的示例。

https://dev-to-uploads.s3.amazonaws.com/uploads/articles/euikebi5cbzwz74iqqv9.png

一个更常见的示例可能是一个项目具有自己的设计系统时,我们需要一个演示应用来查看我们的UI组件,除了主应用程序。

选项2

当我们在同一家公司内拥有两个独立开发的应用程序时,这种情况是这种情况,业务需求表明不同功能是团结的,或者是一个应用程序的所有功能都重复使用。

在这里,多模块化将节省一天,因为我们已经有了一个孤立的代码,该代码已准备好重复使用。同时,这些模块存在于不同的存储库中,彼此之间没有相互关联,并且具有不同的依赖性列表。
我们如何团结不同项目的模块?首先,我们只需将它们上传到相邻目录中,并通过将其他应用程序的模块包括在其中。

project(':feature-list').projectDir = new File('../app_to_be_reused/feature-list')

include ':feature-details'
project(':feature-details').projectDir = new File('../app_to_be_reused/feature-details')

include ':domain'
project(':domain').projectDir = new File('../app_to_be_reused/domain')

不幸的是,如果我们只需要:feature-list,我们还必须将该模块的依赖项添加到我们的项目中,即拉:domain:feature-details

https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f9e3e3cbliejsm5ogmb1.jpg

如果我们没有事先准备好重用项目的模块,即使我们只是尝试导入所需的一切并同步主项目,也会引起很多问题。例如,如果我们可重复使用的:feature-1模块有活动,他们将无法使用主项目中定义的主题。
同样,这两个项目都是不同的GIT存储库,因此我们必须注意在正确的分支上,同时同步模块。 Gradle配置很有可能在RESUS项目中发生了变化,这就是为什么我们需要始终检查我们是否在项目的正确版本中工作的原因。反过来,它导致CI构建问题现在必须获取两个项目,而不是一个项目,并在同一管道中构建和测试两个项目。

git子模型可以对我们有所帮助。它们使我们能够将另一个存储库包括在现有的回购中,并通过依赖项标记项目的正确版本。您可以了解有关here子模型的更多信息。在这种情况下,依赖图将与上一个示例中的图相似。

https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uf4bljovqatrtsp3176r.jpeg

无论如何,您作为另一个应用程序的开发人员必须深入研究另一个团队的代码,而学习曲线很可能很陡峭。请参阅一个示例,其中一个链接另一个项目的模块here

选项3

此选项基于单独的Android库中重复使用应用程序的一部分的出版物,以便其他应用程序可以将其链接并使用它作为其他任何第三方SDK。如果具有依赖关系的应用程序相当模块化,则可以尝试导出所需的模块,然后从另一个应用程序中为其添加输入点。通常,我们使用maven-publish gradle插件来发布纯Java/kotlin库,但是Android Gradle插件最近已经开始支持它,现在我们可以export Android modules,而不仅仅是Java库。




这是一个例子。我们的 dogpapp 由模块appfeature-listfeature-detailsdomain组成。 Feature-list负责打印有关某狗的信息和图像。 domain包括在所有其他模块中重复使用的一般信息。

在某个时候,我们的公司购买了另一家开发 catsapp 的启动业务。现在,业务需求决定我们支持新应用程序,并通过添加相同的屏幕来查看猫的详细信息,例如 dogpapp 。由于应用程序是单独开发的,因此它们具有不同的依赖树,我们不能仅使用前两种方法,即合并代码库或将所需的模块从 dogdapp 链接到 catsapp 。但是我们可以利用图书馆出版物。在这种情况下,我们需要发布feature-details
为此,我们将使用 maven-publish 插件,然后将以下代码添加到feature-details build.gradle

android {
   ...
   publishing {
       singleVariant('release')
   }
}

afterEvaluate {
   publishing {
       publications {
           release(MavenPublication) {
               from components.release
           }
       }
   }
}

singleVariant点,我们只想发布一个构建选项。为了使我们可以同时使用库的调试和发布版本,我们需要使用allVariants()components.default

   ...
   publishing {
      multipleVariants {
         allVariants()
      }
   }
}

afterEvaluate {
   publishing {
       publications {
           allVariants(MavenPublication) {
               from components.default
           }
       }
   }
}

现在,我们可以使用gradle :feature-details:publishToMavenLocal命令发布我们的模块。让我们添加一个本地的Maven仓库到 catsapp ,以便我们可以使用这种依赖性:

settings.gradle:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
       google()
       mavenCentral()
       mavenLocal()
   }
}

现在,我们可以将依赖关系添加到cats_app/build.gradle中的导出库:

dependencies {
   ...
   implementation 'com.example:feature-details:1.0.0'
}

但是,当我们尝试构建 catsapp 时,会发生构建错误,因为构建系统无法找到:domain模块,因此我们也需要发布它。实际上,我们必须递归发布:feature-details模块使用的所有依赖项。为此,删除我们之前添加的代码以发布:feature-details,并将以下内容添加到root build.gradle

subprojects {
   apply plugin: "maven-publish"

   afterEvaluate {
       if (!plugins.hasPlugin("android")) {

           if (plugins.hasPlugin("android-library")) {
               android {
                   publishing {
                       multipleVariants {
                           allVariants()
                       }
                   }
               }
           }

           publishing {
               publications {
                   allVariants(MavenPublication) {
                       afterEvaluate {
                           if (plugins.hasPlugin("java")) {
                               from components.java
                           } else if (plugins.hasPlugin("android-library")) {
                               from components.default
                           }
                       }
                   }
               }
           }
       }
   }

Subprojects {意味着我们要将代码应用于所有supodules。但是我们不想发布主模块:dogs-app,因此我们通过Android插件!plugins.hasPlugin("android")的存在过滤出来。另请注意,:domain是一个Java库,没有构建选项,这就是为什么我们不需要将此信息添加到其中。我们将仅为Android库添加构建选项:

 if (plugins.hasPlugin("android-library")) {
     android {

此外,由于我们具有Java和Android模块,因此我们必须使用不同的出版物方法。如果已将Java插件应用于模块,则将其作为Java库发布,并且在Android Library插件的情况下,我们将发布一组 .AAR 档案,并具有相应的构建选项。

if (plugins.hasPlugin("java")) {
    from components.java
} else if (plugins.hasPlugin("android-library")) {
    from components.default
}

现在,当我们使用同一gradle :feature-details:publishToMavenLocal命令发布我们的库时,所有依赖项都将放入我们本地的Maven存储库中,并且客户端应用程序 catsapp 将在没有问题的情况下构建。您可以找到完整的代码here

你呢?您是否曾经不得不合并不同的应用程序或重复使用另一个团队的Gradle依赖性?请在评论中分享您公司中使用的方法。