Erstellen Sie eine Android-App. Aufgabe mit einem Sternchen



Hallo Habr! Im Sommer sprach ich beim Summer Droid Meetup mit einem Bericht über die Erstellung einer Android-Anwendung. Die Videoversion finden Sie hier: habr.com/ru/company/funcorp/blog/462825 . Und für diejenigen, die gerne mehr lesen, habe ich gerade diesen Artikel geschrieben.

Es geht darum, was es ist - eine Android-Anwendung. Wir werden auf verschiedene Arten sammeln Hallo, Welt !: Beginnen Sie an der Konsole und sehen Sie, was unter der Haube von Build-Systemen passiert, und kehren Sie dann in die Vergangenheit zurück. Erinnern Sie sich an Maven und lernen Sie moderne Bazel- und Buck-Lösungen kennen. Und schließlich ist das alles vergleichbar.

Wir haben über eine mögliche Änderung des Montagesystems nachgedacht, als wir ein neues Projekt gestartet haben. Es schien uns, dass dies eine gute Gelegenheit ist, nach Alternativen zu Gradle zu suchen. Darüber hinaus ist es zu Beginn einfacher, dies zu tun, als ein vorhandenes Projekt zu übersetzen. Die folgenden Gradle-Fehler haben uns zu diesem Schritt geführt:

  • Er hat definitiv Probleme mit der inkrementellen Montage, obwohl er Fortschritte in dieser Richtung sehen kann.
  • er macht schlecht mit sehr großen monolithischen Projekten;
  • es kommt vor, dass ein Dämon sehr lange anfängt;
  • anspruchsvoll an der Maschine, auf der es läuft.

APK


Denken Sie zunächst daran, woraus die Android-Anwendung besteht: kompilierter Code, Ressourcen und AndroidManifest.xml.



Die Quellen befinden sich in der Datei classes.dex (je nach Größe der Anwendung können mehrere Dateien vorhanden sein) in einem speziellen Dex-Format, mit dem die virtuelle Android-Maschine arbeiten kann. Heute ist es KUNST auf älteren Geräten - Dalvik. Außerdem finden Sie den Ordner lib, in dem die nativen Quellen in Unterordnern angeordnet sind. Sie werden abhängig von der Zielprozessorarchitektur benannt, z. B. x86, arm usw. Wenn Sie Exoplayer verwenden, haben Sie wahrscheinlich lib. Und der Ordner aidl, der Interprozess-Kommunikationsschnittstellen enthält. Sie sind nützlich, wenn Sie auf einen Dienst zugreifen müssen, der in einem anderen Prozess ausgeführt wird. Solche Schnittstellen werden sowohl in Android selbst als auch in GooglePlayServices verwendet.

Verschiedene nicht kompilierte Ressourcen wie Bilder befinden sich im Ordner res. Alle kompilierten Ressourcen wie Stile, Linien usw. werden in einer resource.arsc-Datei zusammengeführt. Im Assets-Ordner legen sie in der Regel alles ab, was nicht in Ressourcen passt, z. B. benutzerdefinierte Schriftarten.

Darüber hinaus enthält die APK AndroidManifest.xml. Darin beschreiben wir verschiedene Komponenten der Anwendung, wie z. B. Aktivität, Dienst, verschiedene Berechtigungen usw. Es liegt in binärer Form vor, und um nach innen zu schauen, muss es zuerst in eine für Menschen lesbare Datei konvertiert werden.

KONSOLE


Jetzt, da wir wissen, woraus die Anwendung besteht, können wir versuchen, Hello, world! von der Konsole mit den Tools, die das Android SDK bereitstellt. Dies ist ein ziemlich wichtiger Schritt, um zu verstehen, wie Build-Systeme funktionieren, da sie alle in gewissem Maße auf diese Dienstprogramme angewiesen sind. Da das Projekt in Kotlin geschrieben ist, benötigen wir seinen Compiler für die Befehlszeile. Es ist einfach, separat herunterzuladen.

Die Montage der Anwendung kann in folgende Schritte unterteilt werden:

  • Laden Sie alle Bibliotheken herunter und entpacken Sie sie, von denen das Projekt abhängt. In meinem Fall ist dies die Abwärtskompatibilitätsbibliothek von appcompat, die wiederum vom appcompat-Kern abhängt. Wir pumpen sie also ebenfalls aus.
  • generiere R.java. Diese wunderbare Klasse enthält die Bezeichner aller Ressourcen in der Anwendung und wird verwendet, um im Code darauf zuzugreifen.
  • Wir kompilieren die Quellen in Bytecode und übersetzen sie in Dex, da die virtuelle Android-Maschine nicht weiß, wie sie mit dem üblichen Bytecode arbeiten soll.
  • Wir packen alles in die APK, richten aber zuerst alle inkompressiblen Ressourcen, wie z. B. Bilder, relativ zum Anfang der Datei aus. Dies ermöglicht es dem Preis einer völlig unbedeutenden Vergrößerung der APK, ihre Arbeit erheblich zu beschleunigen. Somit kann das System mithilfe der Funktion mmap () Ressourcen direkt dem RAM zuordnen.
  • Unterschreiben Sie den Antrag. Dieses Verfahren schützt die Integrität der APK und bestätigt die Urheberschaft. Auf diese Weise kann der Play Market beispielsweise überprüfen, ob die Anwendung von Ihnen erstellt wurde.

Skript erstellen
function preparedir() { rm -r -f $1 mkdir $1 } PROJ="src/main" LIBS="libs" LIBS_OUT_DIR="$LIBS/out" BUILD_TOOLS="$ANDROID_HOME/build-tools/28.0.3" ANDROID_JAR="$ANDROID_HOME/platforms/android-28/android.jar" DEBUG_KEYSTORE="$(echo ~)/.android/debug.keystore" GEN_DIR="build/generated" KOTLIN_OUT_DIR="$GEN_DIR/kotlin" DEX_OUT_DIR="$GEN_DIR/dex" OUT_DIR="out" libs_res="" libs_classes="" preparedir $LIBS_OUT_DIR aars=$(ls -p $LIBS | grep -v /) for filename in $aars; do DESTINATION=$LIBS_OUT_DIR/${filename%.*} echo "unpacking $filename into $DESTINATION" unzip -o -q $LIBS/$filename -d $DESTINATION libs_res="$libs_res -S $DESTINATION/res" libs_classes="$libs_classes:$DESTINATION/classes.jar" done preparedir $GEN_DIR $BUILD_TOOLS/aapt package -f -m \ -J $GEN_DIR \ -M $PROJ/AndroidManifest.xml \ -S $PROJ/res \ $libs_res \ -I $ANDROID_JAR --auto-add-overlay preparedir $KOTLIN_OUT_DIR compiledKotlin=$KOTLIN_OUT_DIR/compiled.jar kotlinc $PROJ/java $GEN_DIR -include-runtime \ -cp "$ANDROID_JAR$libs_classes"\ -d $compiledKotlin preparedir $DEX_OUT_DIR dex=$DEX_OUT_DIR/classes.dex $BUILD_TOOLS/dx --dex --output=$dex $compiledKotlin preparedir $OUT_DIR unaligned_apk=$OUT_DIR/unaligned.apk $BUILD_TOOLS/aapt package -f -m \ -F $unaligned_apk \ -M $PROJ/AndroidManifest.xml \ -S $PROJ/res \ $libs_res \ -I $ANDROID_JAR --auto-add-overlay cp $dex . $BUILD_TOOLS/aapt add $unaligned_apk classes.dex rm classes.dex aligned_apk=$OUT_DIR/aligned.apk $BUILD_TOOLS/zipalign -f 4 $unaligned_apk $aligned_apk $BUILD_TOOLS/apksigner sign --ks $DEBUG_KEYSTORE $aligned_apk 


Den Zahlen zufolge stellt sich heraus, dass eine saubere Assembly 7 Sekunden dauert und die inkrementelle Assembly nicht dahinter zurückbleibt, da wir nichts zwischenspeichern und jedes Mal alles neu erstellen.

Maven


Es wurde von den Mitarbeitern der Apache Software Foundation entwickelt, um Java-Projekte zu erstellen. Build-Konfigurationen dafür werden in XML beschrieben. Die frühen Revisionen von Maven wurden von Ant gesammelt und jetzt auf die neueste stabile Version umgestellt.

Vorteile von Maven:

  • Es unterstützt das Zwischenspeichern von Baugruppenartefakten, d.h. Inkrementeller Build sollte schneller als sauber sein.
  • Abhängigkeiten von Drittanbietern können aufgelöst werden. Das heißt, Wenn Sie in der Maven- oder Gradle-Konfiguration eine Abhängigkeit von einer Drittanbieter-Bibliothek angeben, müssen Sie sich keine Gedanken darüber machen, wovon sie abhängt.
  • Es gibt eine Reihe detaillierter Dokumentationen, da diese schon seit einiger Zeit auf dem Markt sind.
  • und es kann ein vertrauter Build-Mechanismus sein, wenn Sie kürzlich vom Backend aus in die Welt der Android-Entwicklung gekommen sind.

Nachteile von Maven:

  • Hängt von der Java-Version ab, die auf dem Computer installiert ist, auf dem die Assembly stattfindet.
  • Das Android-Plugin wird jetzt von Drittentwicklern unterstützt: Ich persönlich halte dies für einen sehr erheblichen Nachteil, da sie eines Tages möglicherweise damit aufhören werden.
  • XML eignet sich aufgrund seiner Redundanz und Umständlichkeit nicht sehr gut zur Beschreibung von Build-Konfigurationen.
  • Nun, und wie wir später sehen werden, läuft es zumindest bei einem Testprojekt langsamer als Gradle.

Zum Erstellen müssen wir pom.xml erstellen, die eine Beschreibung unseres Projekts enthält. Geben Sie in der Kopfzeile die grundlegenden Informationen zum gesammelten Artefakt sowie die Version von Kotlin an.

Erstelle config pom.xml
 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>myapplication</artifactId> <version>1.0.0</version> <packaging>apk</packaging> <name>My Application</name> <properties> <kotlin.version>1.3.41</kotlin.version> </properties> <dependencies> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-stdlib</artifactId> <version>${kotlin.version}</version> </dependency> <dependency> <groupId>com.google.android</groupId> <artifactId>android</artifactId> <version>4.1.1.4</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-maven-plugin</artifactId> <version>${kotlin.version}</version> <executions> <execution> <id>compile</id> <phase>process-sources</phase> <goals> <goal>compile</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>com.simpligility.maven.plugins</groupId> <artifactId>android-maven-plugin</artifactId> <extensions>true</extensions> <configuration> <sdk> <platform>28</platform> <buildTools>28.0.3</buildTools> </sdk> <failOnNonStandardStructure>false</failOnNonStandardStructure> </configuration> </plugin> </plugins> </build> </project> 


In Zahlen ist nicht alles zu rosig. Eine saubere Assembly dauert ungefähr 12 Sekunden, eine inkrementelle - 10. Dies bedeutet, dass Maven Artefakte aus früheren Assemblys irgendwie wiederverwendet, oder meiner Meinung nach ist es wahrscheinlicher, dass das Plug-In zum Erstellen eines Android-Projekts dies verhindert

Jetzt nutzen sie das alles, ich denke, zuallererst sind die Macher des Plugins die Jungs von der Einfachheit. Zuverlässigere Informationen zu diesem Problem wurden nicht gefunden.

Bazel


Ingenieure im Darm von Google haben Bazel erfunden, um ihre Projekte zu erstellen, und es vor relativ kurzer Zeit auf Open Source übertragen. Für die Beschreibung von Build-Configs werden pythonähnliche Skylark oder Starlark verwendet, beide Namen haben einen Platz zu sein. Es wird mit seiner neuesten stabilen Version zusammengebaut.

Vorteile von Bazel:

  • Unterstützung für verschiedene Programmiersprachen. Wenn Sie der Dokumentation glauben, weiß er, wie man Projekte für iOs, Android oder sogar ein Backend sammelt.
  • Kann zuvor gesammelte Artefakte zwischenspeichern
  • in der Lage, mit Maven-Abhängigkeiten zu arbeiten;
  • Bazel hat meiner Meinung nach eine sehr coole Unterstützung für verteilte Projekte. Er kann bestimmte Revisionen von Git-Repositorys als Abhängigkeiten angeben und diese während des Erstellungsprozesses entladen und zwischenspeichern. Um die Skalierbarkeit zu unterstützen, kann Bazel beispielsweise verschiedene Ziele auf Cloud-basierten Build-Servern verteilen, sodass Sie schnell umfangreiche Projekte erstellen können.

Nachteile von Bazel:

  • All dieser Charme ist sehr schwer aufrechtzuerhalten, da die Build-Konfigurationen sehr detailliert sind und die Baugruppe auf einer niedrigen Ebene beschreiben.
  • unter anderem scheint sich Bazel jetzt aktiv zu entwickeln. Aus diesem Grund werden einige Beispiele nicht erfasst, und diejenigen, die erfasst werden, können die veraltete Funktionalität verwenden, die als veraltet markiert ist.
  • Die Dokumentation lässt jetzt auch zu wünschen übrig, insbesondere im Vergleich zu Gradle.
  • Bei kleinen Projekten kann das Aufwärmen und Analysieren von Build-Konfigurationen länger dauern als die Montage selbst, was meiner Meinung nach nicht gut ist.

Konzeptionell besteht die grundlegende Bazel-Konfiguration aus WORKSPACE, in dem wir alle möglichen globalen Dinge für ein Projekt beschreiben, und BUILD, das direkt Ziele für die Montage enthält.
Beschreiben wir den WORKSPACE. Da wir ein Android-Projekt haben, konfigurieren wir zunächst das Android SDK. Außerdem wird hier eine Regel zum Entladen von Konfigurationen importiert. Da das Projekt in Kotlin geschrieben ist, müssen wir die Regeln dafür angeben. Hier tun wir dies und verweisen auf eine bestimmte Revision direkt aus dem Git-Repository.

ARBEITSPLATZ
 android_sdk_repository( name = "androidsdk", api_level = 28, build_tools_version = "28.0.3" ) load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # # KOTLIN RULES # RULES_KOTLIN_VERSION = "990fcc53689c8b58b3229c7f628f843a60cb9f5c" http_archive( name = "io_bazel_rules_kotlin", url = "https://github.com/bazelbuild/rules_kotlin/archive/%s.zip" % RULES_KOTLIN_VERSION, strip_prefix = "rules_kotlin-%s" % RULES_KOTLIN_VERSION ) load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kotlin_repositories", "kt_register_toolchains") kotlin_repositories() kt_register_toolchains() 


Jetzt fangen wir mit dem BUILD an.

Zuerst importieren wir die Regel zum Zusammenstellen von Kotlin und beschreiben, was wir sammeln möchten. In unserem Fall ist dies eine Android-Anwendung, daher verwenden wir android_binary, wo wir das Manifest, das minimale SDK usw. festlegen. Unsere Anwendung hängt von der Quelle ab, daher erwähnen wir sie in Deps und gehen weiter zu dem, was sie sind und wo sie zu finden sind. Der Code hängt auch von den Ressourcen und der Appcompat-Bibliothek ab. Für Ressourcen verwenden wir das übliche Ziel zum Zusammenstellen von Android-Quellen, weisen ihm jedoch nur Ressourcen ohne Java-Klassen zu. Und wir beschreiben einige Regeln, die Bibliotheken von Drittanbietern importieren. Außerdem wird appcompat_core erwähnt, von dem appcompat abhängt.

BAUEN
 load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_android_library") android_binary( name = "app", custom_package = "com.example.myapplication", manifest = "src/main/AndroidManifest.xml", manifest_values = { "minSdkVersion": "15", }, deps = [ ":lib", ], ) kt_android_library( name = "lib", srcs = glob(["src/main/java/**/*"]), deps = [ ":res", ":appcompat", ], ) android_library( name = "res", resource_files = glob(["src/main/res/**/*"]), manifest = "src/main/AndroidManifest.xml", custom_package = "com.example.myapplication", ) aar_import( name = "appcompat", aar = "libs/appcompat.aar", deps = [ ":appcompat_core", ] ) aar_import( name = "appcompat_core", aar = "libs/core.aar", ) 


In Zahlen für ein so kleines Projekt sieht alles traurig aus. Mehr als eine halbe Minute für einen sauberen Build Hallo Welt! - sehr viel. Inkrementelle Bauzeiten sind ebenfalls alles andere als perfekt.

Bazel wird von seinen Entwicklern (Google) für einige ihrer Projekte verwendet, einschließlich Serverprojekten, sowie für Dropbox und Huawei, die mobile Anwendungen für sie sammeln. Und der berüchtigte Dolch 2 geht auch nach Bazel.

Buck


Es wurde von Überläufern von Google bis Facebook erfunden. Er benutzte Python, um die Konfigurationen zu beschreiben, und wanderte dann zu der heute erwähnten Lerche aus. Er benutzt plötzlich das Ameisensystem.

Buck Pros:

  • unterstützt verschiedene Programmiersprachen und kann sowohl Andriod als auch iOS erstellen;
  • Kann zuvor gesammelte Artefakte zwischenspeichern
  • Buck hat eine eigene Dex-Implementierung erstellt, die schneller als die Standardimplementierung funktioniert und mit dem Systemdämon hängt. So sparen sie Zeit bei der Dex-Initialisierung. Ingenieure haben wirklich viel optimiert. Beispielsweise sammelt Buck keinen Code, der von der Bibliothek abhängt, wenn sich die Schnittstelle beim Ändern der Bibliotheksinternale nicht geändert hat. Ähnliches gilt für Ressourcen: Wenn sich die Bezeichner nicht geändert haben, wird der Code beim Ändern von Ressourcen nicht wieder zusammengesetzt.
  • Es gibt ein Plugin, das Buck hinter der Gredlovsky-Konfiguration verstecken kann. Das heißt, Sie erhalten so etwas wie ein normales Gradle-Projekt, das tatsächlich über Buck erstellt wird.

Nachteile Buck:

  • es ist so schwer zu pflegen wie Bazel. Das heißt, hier ist es auch notwendig, Regeln auf niedriger Ebene zu beschreiben, die den Montageprozess klar beschreiben;
  • Buck kann Maven-Abhängigkeiten unter anderem nicht alleine auflösen.

Also, was macht die Assembly-Konfiguration für Hallo Welt! durch Geld? Hier beschreiben wir eine Konfigurationsdatei, in der wir angeben, dass wir ein Android-Projekt erstellen möchten, das mit einem Debug-Schlüssel signiert wird. Die Anwendung hängt ebenfalls von der Quell-Lib im deps-Array ab. Als nächstes kommt das Ziel mit den Signatureinstellungen. Ich verwende einen Debit-Schlüssel, der mit dem Android SDK geliefert wird. Unmittelbar danach folgt das Ziel, das die Quellen von Kotlin für uns sammelt. Wie bei Bazel hängt es von Ressourcen und Kompatibilitätsbibliotheken ab.

Wir beschreiben sie. In Buck gibt es ein separates Ziel für Ressourcen, daher sind Fahrräder nicht nützlich. Im Folgenden finden Sie die Regeln für heruntergeladene Bibliotheken von Drittanbietern.

BAUEN
 android_binary( name = 'app', manifest = 'src/main/AndroidManifest.xml', manifest_entries = { 'min_sdk_version': 15, }, keystore = ':debug_keystore', deps = [ ':lib', ], ) keystore( name = 'debug_keystore', store = 'debug.keystore', properties = 'debug.keystore.properties', ) android_library( name = 'lib', srcs = glob(['src/main/java/*.kt']), deps = [ ':res', ':compat', ':compat_core', ], language = 'kotlin', ) android_resource( name = 'res', res = "src/main/res", package = 'com.example.myapplication', ) android_prebuilt_aar( name = 'compat', aar = "libs/appcompat.aar", ) android_prebuilt_aar( name = 'compat_core', aar = "libs/core.aar", ) 


Das Ganze läuft sehr zügig. Eine saubere Baugruppe dauert etwas mehr als 7 Sekunden, während eine inkrementelle Baugruppe 200 Millisekunden lang vollständig unsichtbar ist. Ich denke, das ist ein sehr gutes Ergebnis.

Das macht Facebook. Zusätzlich zu ihrer Flaggschiff-Anwendung sammeln sie Facebook Messenger für sie. Und Uber, der mit Lyft das Plugin für Gradle und Airbnb erstellt hat.

Schlussfolgerungen


Nachdem wir über jedes Build-System gesprochen haben, können wir sie anhand des Beispiels Hallo, Welt! Die Konsolenbaugruppe überzeugt durch ihre Stabilität. Die Ausführungszeit des Skripts vom Terminal aus kann als Referenz für die Zusammenstellung sauberer Builds angesehen werden, da die Kosten von Drittanbietern für das Parsen von Skripten hier minimal sind. In diesem Fall würde ich Maven als offensichtlichen Außenseiter für eine äußerst unbedeutende Zunahme der inkrementellen Montage bezeichnen. Bazel analysiert Konfigurationen sehr lange und initialisiert sie: Es besteht die Idee, dass die Initialisierungsergebnisse irgendwie zwischengespeichert werden, da ihr inkrementeller Build viel schneller als sauber ist. Buck ist der unbestrittene Anführer dieser Sammlung. Sehr schnelle saubere und inkrementelle Montage.



Vergleichen Sie nun die Vor- und Nachteile. Ich werde Maven nicht in den Vergleich einbeziehen, da es eindeutig gegen Gradle verliert und auf dem Markt fast nie verwendet wird. Ich vereine Buck und Bazel, weil sie ungefähr die gleichen Vor- und Nachteile haben.

Also, über Gradle:

  • Das erste und meiner Meinung nach wichtigste ist, dass es einfach ist. Sehr einfach;
  • Entladen und Entladen von Abhängigkeiten;
  • Für ihn gibt es viele verschiedene Schulungen und Dokumentationen;
  • aktiv von Google und der Community unterstützt. Hervorragende Integration in Android Studio, das aktuelle Flaggschiff-Entwicklungstool. Alle neuen Funktionen zum Erstellen einer Android-Anwendung werden zuerst in Gradle angezeigt.

Über Buck / Bazel:

  • kann definitiv sehr schnell sein im Vergleich zu Gradle. Ich glaube, dass sich dies besonders bei sehr großen Projekten bemerkbar macht
  • Sie können ein Projekt behalten, in dem Quellcodes für iOS und Android vorhanden sind, und diese mit einem Build-System zusammenstellen. Dadurch können einige Teile der Anwendung zwischen Plattformen fummeln. So läuft beispielsweise Chrom.
  • gezwungen, Abhängigkeiten im Detail zu beschreiben und so den Entwickler buchstäblich zu zwingen, multimodular zu sein.

Vergessen Sie nicht die Nachteile.

Gradle zahlt für seine Einfachheit, indem es langsam und ineffizient ist.
Buck / Bazel hingegen leidet aufgrund seiner Geschwindigkeit unter der Notwendigkeit, den Erstellungsprozess in Konfigurationen detaillierter zu beschreiben. Nun, da sie vor relativ kurzer Zeit auf dem Markt erschienen sind, gibt es nicht viele Dokumentationen und verschiedene Spickzettel.

iFUNNY


Vielleicht haben Sie eine Frage, wie wir iFunny sammeln. Genau wie viele andere - mit Gradle. Und dafür gibt es Gründe:

  1. Es ist noch nicht klar, welchen Gewinn an Montagegeschwindigkeit dies für uns bringen wird. Ein sauberer Build von iFunny dauert fast 3 Minuten und ist inkrementell - ungefähr eine Minute, was eigentlich nicht sehr lang ist.
  2. Buck- oder Bazel-Build-Konfigurationen sind schwieriger zu warten. Im Fall von Buck müssen Sie auch die Relevanz der verbundenen Bibliotheken und der Bibliotheken überwachen, von denen sie abhängen.
  3. Es ist banal teuer, ein bestehendes Projekt von Gradle nach Buck / Bazel zu übertragen, insbesondere unter Bedingungen unverständlichen Gewinns.

Wenn Ihr Projekt länger als 45 Minuten dauert und etwa 20 Mitarbeiter im Android-Entwicklungsteam sind, ist es sinnvoll, über eine Änderung des Build-Systems nachzudenken. Wenn Sie und Ihr Freund ein Startup sägen, verwenden Sie Gradle und lassen Sie diese Gedanken fallen.



Gerne diskutiere ich die Aussichten für Alternativen zu Gradle in den Kommentaren!
Link zum Projekt

Source: https://habr.com/ru/post/de469771/


All Articles