Android环境

前言


从遥远的2012年开始,我记得在哈勃(Habr)的露天场所:


...如果从一开始就没有任何情况,则设备将保留
未完成,将灰尘落在架子上...

主题远非硬件组件。 分析我的问题后,我对这一判断的正确性深信不疑,并试图将东西整理在满是灰尘的架子上。


最近,一位客户要求我向他的项目添加对多项服务的支持。 任务是我需要连接“ A”服务,并在将应用程序投入生产之前,在测试环境中运行该服务。 我决定分析以前的决定,然后……感到震惊。


对于各种类型的程序集,我使用了各种配置文件以及环境变量的描述。 但是问题在于,仅要将值转发到实际代码中,就必须为每种类型编写相同的代码。


问题


Google使我们能够转发每个值的自定义值
组装


android { //... buildTypes { release { buildConfigField("String", "HOST_URL", "\"prod.com\"") } debug { buildConfigField("String", "HOST_URL", "\"debug.com\"") } } } 

在分析了build.gradle脚本之后,android工具将从buildTypesproductFlavors中获取所有buildConfigFileds的值,并为每种类型的程序集生成BuildConfig文件:


 public final class BuildConfig { //... // Fields from build type: release public static final String HOST_URL = "prod.com"; } 

乍看没有问题。 尤其是当您的应用程序中没有那么多的风味和自定义值时。 在我的项目中,有20多种环境和3种环境(内部/ alpha /生产环境)。 显然,对我来说只有一个问题-摆脱样板。


同样重要的问题是环境变量的值不应反映在您的项目中。 即使在配置文件中。 您必须通过VCS检查build.gradle配置。 但是您不应该直接注册密钥,为此,您需要第三方机制(例如,文件,CI服务)。 在我的实践中,有几个项目无法进行发布生产装配,因此我无法访问某些库的值。 这已经是一个业务问题,为了它的利益,不要花费不必要的成本。 在调试或内部测试期间,不应使用用于生产的密钥。


解决问题的方法


在一个旧项目中,为了存储环境变量的值,我们使用了简单的.properties文件,该文件通过经典键:值映射提供对字段的访问。 这种方法不能解决绑定问题。 但是它解决了数据传递的问题,应该应用。 另外,我们可以将.properties文件作为某种数据提供合同的基础。


如果再回头,我们有一个中间步骤:从buildConfigFieldBuildConfig类的字段。 但是,这是谁呢? 一切都很老套,您在所有Android项目中绝对连接的gradle插件对此负责。


 apply plugin: "com.android.application" 

由他负责的事实是,在分析了build.gradle文件之后,将为每种风味具有自己的字段集生成BuildConfig类。 这样,我可以编写自己的药物来扩展com.android.application的功能并保存
我从这个头痛。


该问题的解决方案如下:提供合同,
它将描述所有程序集的所有键和值。
将配置文件扩展为子类型。 一切都交给插件。


画图

解决方案


上面我们已经找到了解决方案的结构,剩下要做的就是将其全部变为现实。 似乎可以通过简单的构建文件扩展名解决简单的解决方案和问题。 最初,我是这样做的。


显示解决方案
 ```groovy class Constants { // Environments properties path pattern, store your config files in each folders of pattern static final CONFIG_PROPERTY_PATTERN = "config/%s/config.properties" } android.buildTypes.all { buildType -> buildConfigFields(buildType, buildType.name) } android.applicationVariants.all { appVariant -> buildConfigFields(appVariant, appVariant.flavorName) } private def buildConfigFields(Object variant, String variantName) { def properties = getProperties(variantName) properties.each { key, value -> variant.buildConfigField( parseValueType(value), toConfigKey(key), value ) } } // Convert config property key to java constant style private def toConfigKey(String key) { return key.replaceAll("(\\.)|(-)", "_") .toUpperCase() } // Parse configuration value type private def parseValueType(String value) { if (value == null) { throw new NullPointerException("Missing configuration value") } if (value =~ "[0-9]*L" ) { return "Long" } if (value.isInteger()) { return "Integer" } if (value.isFloat()) { return "Float" } if ("true" == value.toLowerCase() || "false" == value.toLowerCase()) { return "Boolean" } return "String" } private def getProperties(String variantName) { def propertiesPath = String.format( Constants.CONFIG_PROPERTY_PATTERN, variantName ) def propertiesFile = rootProject.file(propertiesPath) def properties = new Properties() // Do nothing, when configuration file doesn't exists if (propertiesFile.exists()) { properties.load(new FileInputStream(propertiesFile)) } return properties } ``` 

在这里,立即出现了我从未想到的那些困难-一个尘土飞扬的团。我决定将我的决定“卖”给我的同事。 我准备了一个基座,开始讨论此事,然后……我意识到我们都是人,程序员是懒惰的人。 没有人愿意在他的项目中嵌入未知的代码,是否需要研究,永久阅读? 如果他不工作怎么办? 如果他做错了其他事情怎么办? 这很古怪,但我不认识他,也不清楚如何与他合作。 而且脚本已经搬到Kotlin很久了,我无法从凹槽中移出,依此类推。


最有趣的是,所有这些判断已经来自我,因为 我意识到这种解决方案的集成不适合我。 另外,我注意到我确实想改进的几点。 在项目A中实现了该解决方案之后,我想在项目B中为其提供支持。只有一种解决方法-您需要编写一个插件。


插件及其向用户的远程交付将解决什么问题?


  • 懒惰的程序员的问题。 我们懒得深入研究问题的根源和解决问题的可能方法。 对于我们来说,采取之前已经做的事情并使用它要容易得多。
  • 支持。 它包括代码支持,其发展和机会的扩展。 解决问题时,我仅在代码中解决了probrosvarivnyh环境,完全忘记了转发到资源的可能性。
  • 代码质量。 有一种观点认为,一些开发人员甚至不会查看测试未涵盖的开源代码。 现在是2019年,我们可以轻松连接服务以跟踪代码https://sonarcloud.iohttps://codecov.io/
  • 配置。 构建文件扩展名迫使我检查此代码并手动进行更改。 就我而言,我并不需要总是使用buildTypesproductFlavors的配置,我一次要一件东西或全部。
  • 尘土飞扬的架子清洁。 我终于清理了其中一个,并且能够继续这个小房间的决定。

编写插件时,我不会详细介绍和遇到问题,它会带来新的话题。 您可以通过其集成来做出决定,提出您的想法或做出自己的贡献
在这里

Source: https://habr.com/ru/post/zh-CN462751/


All Articles