Seit vielen Jahren entwickeln wir bei ElevateX Android-Apps für Start-ups ebenso wie für Fortune-500-Unternehmen. Deshalb sind wir davon überzeugt, dass jede erfolgreiche Android-App die folgenden übergeordneten Designziele haben sollte:
- Angenehme Nutzererfahrung mit einer modernen, flüssigen und reaktionsschnellen Benutzeroberfläche
- Offline-Unterstützung für instabile Netze, z. B. in der U-Bahn
- Wartbare, saubere Codebasis mit hoher Testabdeckung
- Möglichkeit, die UI für schnelles Prototyping per A/B-Testing zu prüfen
- Die App kontinuierlich an unterschiedliche Nutzergruppen ausrollen zu können
- Push-Benachrichtigungen und App-Deep-Links, um Nutzer erneut zu aktivieren
Wir bekommen selten die Gelegenheit, eine App wirklich von Grund auf neu zu starten. Das ist aber kein Grund, bei veralteten Bibliotheken, Frameworks, Vorgehensweisen oder ungetestetem Code zu bleiben. Stattdessen solltest du unsere Empfehlungen auf dein aktuelles Projekt anwenden, wenn du zum Beispiel neue Funktionen oder neue App-Bereiche einführst. So kannst du deine bestehende Codebasis unangetastet lassen und trotzdem Schritt für Schritt die Gesamtqualität deiner App verbessern.
Im Folgenden skizzieren wir das Android-Projekt-Setup, auf das wir heute - im Februar 2019 - abzielen.
Wir beginnen mit (1) den Grundlagen eines Android-Projekts, gefolgt von (2) Infrastruktur-Empfehlungen, die über das bloße Schreiben wartbaren Quellcodes hinausgehen. Danach erläutern wir (3) einige Architekturentscheidungen für Apps, die sich in früheren Projekten bewährt haben und (4) automatisiertes Testen ermöglichen. Um all das leichter umzusetzen, kommen anschließend (5) einige hilfreiche Bibliotheken und Werkzeuge. Zum Schluss geben wir (6) noch ein paar zusätzliche Tipps.
1. Grundlegendes Android-Projekt-Setup
- Nutze das aktuelle Gradle-Build-Tool und dessen Abhängigkeitsverwaltung
- Richte Build-Varianten für z. B. Development und Production ein
- Nutze ProGuard für Release-Versionen
- Das minimale API-Level wird meist vom Kunden vorgegeben, sollte aber nicht unter Android 5 (Level 21) liegen
- Nutze AndroidX, um die Lücken zwischen den API-Leveln zu überbrücken - achte darauf, auf AndroidX umzustellen und nicht die veralteten, versionsspezifischen Bibliotheken zu verwenden.
- Bevorzuge Kotlin gegenüber Java
- Nutze für Android auch die Kotlin Extension (KTX)
- Halte dich an den Android-Kotlin-Guide
- Nutze Lint-Tools, um potenzielle Programmierfehler, Bugs, Stilprobleme usw. zu markieren
2. Infrastruktur
- Nutze Git zur Versionsverwaltung mit master-, develop- und feature-Branches. Versehe Releases mit Tags und erzwinge das Zusammenführen zwischen Branches nur über einen Pull Request (PR).
- Nutze Continuous Integration (CI) - z. B. durch Jenkins, um bei jedem Commit Builds und (Unit-)Tests auszuführen. Mindestens der Nightly Build sollte ebenfalls Integrationstests ausführen.
- Optional kann SonarQube eingerichtet werden, um Code Smells zu finden und die Testabdeckung in Form eines Quality Gates für jedes Release durchzusetzen.
- Verlange PR-Reviews durch mindestens einen weiteren Entwickler und erlaube das Zusammenführen von Branches nur, wenn der CI-Build des Branches erfolgreich ist und als „grün“ markiert wurde.
- Richte AppCenter ein (früher HockeyApp), um App-Versionen und -Varianten an benutzerdefinierte Verteilungsgruppen ausliefern zu können (z. B. Entwickler, Tester, Kunden, Beta-User, …).
- AppCenter bietet außerdem Crash-Reports und Analysen, die beim Beheben von Fehlern helfen, die von Nutzern gemeldet werden
3. Android-Architektur
- Nutze das Model-View-ViewModel-(MVVM)-Architekturmuster, da es Verantwortlichkeiten sauber trennt und Unit-Tests der Geschäftslogik der App ermöglicht
- Context oder andere Android-Ressourcen sollten niemals in Models oder ViewModels verwendet werden, um deren 100%ige Unit-Testbarkeit sicherzustellen
- Führe „Services“ ein, die ViewModels Funktionen wie das Empfangen und Persistieren von Daten bereitstellen
- Führe Navigatoren ein, um die Navigation zwischen Views zentral zu steuern, indem Intents verwendet werden (keine riesigen Datenmengen als Parcelable übergeben, sondern stattdessen IDs auf lokal gespeicherte Informationen verwenden)
- Alternativ kann das Model-View-Intent-(MVI)-Muster verwendet werden, da es von Natur aus unveränderliche Daten/Objekte mit einem unidirektionalen Datenfluss erzwingt
- „Package by feature“ sorgt für klare Verantwortlichkeiten und ermöglicht den möglichen Einsatz von Android Instant App
- Führe ein gemeinsames Modul für Datenmodelle und allgemeine Funktionen ein (z. B. Datums-Parsing)
- Beachte jederzeit den Android-Lifecycle und nutze die Lifecycle-aware Components von Android Jetpack
- Erstelle eigene Views, um häufig verwendete Elemente zu kapseln - z. B. einen kundenspezifischen Date Picker
- Nutze Dependency Injection und erzeuge den Dependency-Graphen automatisch
- Das verbessert die Testbarkeit, da Mock-Objekte verwendet werden können
- Nutze RxJava
- Da Android-Apps stark von asynchronen Ereignissen geprägt sind und eine App vom System jederzeit unterbrochen werden kann, hat sich RxJava als sehr wertvoll erwiesen (mehr dazu weiter unten)
- Nutze die Shared Preferences, den Dateispeicher und die SQLite-Datenbank von Android, um Daten lokal zu speichern
- Nutze Android-Ressourcendateien, um Themes, Dimensionen, Bilder, Strings usw. zu definieren, und verwende die jeweiligen Unterordner für Lokalisierung und Build-Varianten
4. Tests
- Nutze JUnit so oft wie möglich - es ist schnell und gut unit-testbarer Code ist ein guter Indikator für eine saubere Trennung von Verantwortlichkeiten innerhalb der App
- Nutze Espresso für UI- und Integrationstests
- Füge mindestens einen Test hinzu, der prüft, ob nach dem Start der App der Hauptbildschirm angezeigt wird
- Der Monkey Runner kann verwendet werden, um die App unter Last zu testen
5. Android-Bibliotheken / externe Werkzeuge
- Für Dependency Injection empfehlen wir Koin für reine Kotlin-Projekte, alternativ Dagger2
- Definiere mindestens einen globalen App-Scope und einen activity-spezifischen Scope - füge bei Bedarf weitere, spezifischere Scopes hinzu
- Room - Android Jetpacks Persistenzbibliothek - ist der bevorzugte Weg, um den vollen Funktionsumfang von SQLite zu nutzen
- Die Gson-Bibliothek eignet sich hervorragend für die JSON-Konvertierung
- Retrofit für REST-(Backend-)Aufrufe - es nutzt OkHttp im Hintergrund
- Richte OkHttp so ein, dass Backend-Antworten und heruntergeladene Bilder im Cache gespeichert werden
- Glide als Bildbibliothek
- Nutze Cache und Bildtransformationen, wenn Bilder in der UI angezeigt werden, um die Performance zu verbessern und mobile Daten nicht unnötig zu verbrauchen
- RxJava 2
- Datenflüsse lassen sich damit sehr gut und asynchron modellieren - Ereignisse/Daten können verkettet, gefiltert, zusammengesetzt usw. werden, Threading kann explizit definiert werden und Observer können Ereignisse abonnieren, die sie interessieren
- Führe ein
RxBinderUtilein, das Subscriptions nachverfolgt und lifecycle-aware ist, damit es aktive Subscriptions bei entsprechenden Android-Lifecycle-Ereignissen beenden kann
- Wir raten davon ab, Data Binding zu verwenden, da die Vorteile die längere Build-Zeit und andere Nachteile nicht rechtfertigen. Mit Kotlin können Views über ihre ID auf UI-Elemente zugreifen, ohne
findViewByIdzu verwenden.
6. Sonstiges
- UI-Performance
- Nutze
RecyclerViewfür jede Art von Listen - Vermeide tiefe Verschachtelung in Layout-Dateien und verwende stattdessen
ConstraintLayout
- Nutze
- Analytics
- Es ist bereits Teil von AppCenter, kann aber sinnvoll sein, z. B. Google Analytics für Remarketing über Google AdWords einzuführen. Bitte beachte dabei die DSGVO-Anforderungen. Der Nutzer muss für diese Art von Tracking ausdrücklich zustimmen. Das gilt auch für den Einsatz von Facebook-SDK-Integrationen für Remarketing- oder Single-Sign-On-Zwecke.
- Für Push-Benachrichtigungen empfehlen wir Firebase Messaging (auch für iOS verfügbar)
- Authentifizierung
- Nutze die Authentifizierungs-Interceptor von Retrofit, um z. B. OAuth-Authentifizierung zu handhaben
- Für eine angenehme Nutzererfahrung sollte der Nutzer Single Sign-on über Google, Facebook usw. verwenden können
- Berechtigungsverwaltung
- Da der Nutzer Berechtigungen für die App, z. B. zum Zugriff auf seinen Standort, ausdrücklich freigeben muss, sollten die Kern-Use-Cases keine dieser Plattformfunktionen voraussetzen - sie sollten eher ein Nice-to-have für eine bessere User Experience sein
Fazit
Ein solches Projekt-Setup hat uns geholfen, herausragende Apps für unsere Kunden zu liefern, und wir teilen unsere Erfahrungen gern mit dir. Was ist dein wichtigstes Learning daraus? Schick uns gern Feedback und eine E-Mail an android@elevatex.de.
Wenn du mehr über Android-Entwicklung erfahren möchtest, schau dir unseren Blogbeitrag über Ressourcen an, denen jeder Android-Entwickler folgen sollte an.





