Vorwort
Ab dem fernen Jahr 2012 erinnere ich mich in den offenen Räumen von Habr an den Kommentar:
... wenn es von Anfang an keinen Fall gibt, bleibt das Gerät
unvollendet, wird Staub auf ein Regal fallen ...
Das Thema ist weit entfernt von einer Hardwarekomponente. Als ich mein Problem analysierte, wurde ich von der Richtigkeit dieses Urteils überzeugt und versuchte, die Dinge in meinem staubigen Regal in Ordnung zu bringen.
Kürzlich wurde ich von einem Kunden angesprochen, der mich bat, seinem Projekt Unterstützung für mehrere Dienste hinzuzufügen. Die Aufgabe bestand darin, den Dienst "A" zu verbinden und diesen Dienst in einer Testumgebung auszuführen, bevor die Anwendung in Betrieb genommen wurde. Ich beschloss, meine früheren Entscheidungen zu analysieren und ... war entsetzt.
Für verschiedene Arten von Assemblys habe ich verschiedene Konfigurationsdateien mit der Beschreibung von Umgebungsvariablen verwendet. Das Problem war jedoch, dass nur um den Wert in den realen Code weiterzuleiten, für jeden Typ derselbe Code geschrieben werden musste.
Das Problem
Google gibt uns die Möglichkeit, benutzerdefinierte Werte für jeden weiterzuleiten
Montage .
android { //... buildTypes { release { buildConfigField("String", "HOST_URL", "\"prod.com\"") } debug { buildConfigField("String", "HOST_URL", "\"debug.com\"") } } }
Nach der Analyse des Skripts build.gradle übernehmen die Android-Tools alle Werte von buildConfigFileds aus buildTypes und productFlavors und generieren BuildConfig- Dateien für jeden Assembly-Typ:
public final class BuildConfig {
Kein Problem auf den ersten Blick. Besonders wenn Ihre Anwendung nicht so viele Geschmacksrichtungen und benutzerdefinierte Werte enthält. In meinem Projekt gab es> 20 und 3 Umgebungen (intern / Alpha / Produktion). Offensichtlich gab es für mich nur ein Problem - die Kesselplatte loszuwerden.
Ein ebenso wichtiges Problem ist, dass die Werte von Umgebungsvariablen nicht in Ihrem Projekt berücksichtigt werden sollten. Auch in der Konfigurationsdatei. Sie müssen Ihre build.gradle- Konfiguration über VCS überprüfen. Sie sollten Ihre Schlüssel jedoch nicht direkt registrieren. Dazu benötigen Sie einen Mechanismus eines Drittanbieters (z. B. eine Datei, die Dienste Ihres CI). In meiner Praxis gab es mehrere Projekte, bei denen ich für die Release-Produktionsassemblierung keinen Zugriff auf die Werte einiger Bibliotheken hatte. Dies ist bereits ein Geschäftsproblem und im Interesse, keine unnötigen Kosten zu verursachen. Sie sollten beim Debuggen oder beim internen Testen keine für die Produktion bestimmten Schlüssel verwenden.
Weg, um das Problem zu lösen
In einem der alten Projekte haben wir zum Speichern der Werte von Umgebungsvariablen einfache .properties- Dateien verwendet, die den Zugriff auf die Felder über die klassische key: value-Zuordnung ermöglichten. Dieser Ansatz löst das Bindungsproblem nicht. Aber es löst das Problem der Datenlieferung, das angewendet werden sollte. Darüber hinaus können wir .properties- Dateien als Grundlage für eine bestimmte Art von Datenbereitstellungsvertrag verwenden.
Wenn wir ein wenig zurückgehen, haben wir einen Zwischenschritt: von buildConfigField zum Feld der BuildConfig- Klasse. Aber wer macht das? Alles ist ziemlich kitschig, das Gradle-Plugin, das Sie in allen Android-Projekten absolut verbinden, ist dafür verantwortlich.
apply plugin: "com.android.application"
Er ist dafür verantwortlich, dass nach der Analyse Ihrer build.gradle- Datei die BuildConfig- Klasse für jede Variante mit eigenen Feldern generiert wird. Auf diese Weise kann ich meine eigene Medizin schreiben, die die Funktionen von com.android.application erweitert und speichert
mich von diesen Kopfschmerzen.
Die Lösung des Problems lautet wie folgt: Bereitstellung eines Vertrags,
Hier werden alle Schlüssel und Werte für alle Assemblys beschrieben.
Erweitern Sie Konfigurationsdateien in Untertypen. Gib alles zum Plugin.

Lösung
Oben haben wir die Struktur der Lösung herausgefunden. Das einzige, was wir noch tun müssen, ist, alles zum Leben zu erwecken. Es scheint, dass eine triviale Lösung und ein Problem durch eine einfache Dateierweiterung gelöst werden können. Anfangs habe ich das getan.
Lösung enthüllen ```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 } ```
Und hier traten sofort diese Schwierigkeiten auf, an die ich nicht gedacht hatte - ein staubiges Regiment. Ich beschloss, meine Entscheidung an meine Kollegen zu "verkaufen". Ich habe ein Dock vorbereitet, die Angelegenheit zur Diskussion gestellt und ... Ich habe festgestellt, dass wir alle Menschen sind und Programmierer faule Menschen. Niemand möchte einen ihm unbekannten Code in das Projekt einbetten. Muss er studiert und endgültig gelesen werden? Was ist, wenn er nicht arbeitet? Was ist, wenn er etwas anderes falsch macht? Das ist großartig, aber ich kenne ihn nicht und es ist nicht klar, wie ich mit ihm arbeiten soll. Und ein Skript ist schon lange nach Kotlin gezogen, und ich kann nicht aus Grooves portieren und so weiter.
Das Interessanteste ist, dass all diese Urteile bereits von mir stammen, weil Mir wurde klar, dass eine solche Integration der Lösung nicht zu mir passt. Außerdem habe ich einige Punkte bemerkt, die ich wirklich verbessern möchte. Nachdem ich die Lösung in Projekt A implementiert habe, möchte ich sie in Projekt B unterstützen. Es gibt nur einen Ausweg - Sie müssen ein Plug-In schreiben.
Und welche Probleme werden das Plug-In und seine Remote-Lieferung an den Benutzer lösen?
- das Problem eines faulen Programmierers. Wir sind zu faul, um die Wurzel des Problems und mögliche Lösungswege zu untersuchen. Es ist für uns viel einfacher, etwas, das bereits vor Ihnen erledigt wurde, zu verwenden.
- Unterstützung. Es umfasst Code-Unterstützung, die Entwicklung und Erweiterung von Möglichkeiten. Bei der Lösung meines Problems habe ich nur probrosvarivnyh-Umgebungen nur im Code gelöst und dabei die Möglichkeit der Weiterleitung an Ressourcen völlig vergessen.
- Codequalität. Es gibt eine Meinung, dass einige Entwickler nicht einmal Open Source Code betrachten, der nicht durch Tests abgedeckt wird. Es ist 2019 und wir können problemlos Dienste verbinden, um die Qualität des Codes https://sonarcloud.io oder https://codecov.io/ zu verfolgen.
- Konfiguration. Die Build-Dateierweiterung zwingt mich, diesen Code zu untersuchen und Änderungen manuell vorzunehmen. In meinem Fall muss ich die Konfiguration nicht immer für buildTypes oder productFlavors verwenden , ich möchte eine Sache oder alle gleichzeitig.
- staubige Regalreinigung. Ich habe endlich einen von ihnen aufgeräumt und konnte diese Entscheidung meines kleinen Zimmers fördern.
Ich werde beim Schreiben des Plugins nicht auf Details und Probleme eingehen, sondern ein neues Thema ansprechen. Sie können mit seiner Integration eine Entscheidung treffen, Ihre Idee vorschlagen oder Ihren Beitrag leisten
hier .