Besonderheiten der Android-Programmierung im Jahr 2017 – ein praktischer Exkurs

Wer bisher ein Teufelslied über Fragmentierung im Mobilbereich sang, erntete von diensterfahrenen Entwicklern ein müdes Lächeln. Warum sich das nun ändert und was Android-Entwickler sonst so vorantreiben sollte.

Know-how  –  0 Kommentare
Besonderheiten der Android-Programmierung im Jahr 2017 – ein praktischer Exkurs

Schon früh gab es verschieden große Mobilgeräte, die angesichts der Handgrößen ihrer User unterschiedliche Displayauflösungen mitbrachten. Während man zu Beginn die Logik zur Größenanpassung von Hand realisieren musste, erledigt Android das dank Layouts und ähnlicher Aspekte automatisch. Auch wenn Handera und Sony den einen oder anderen Eigenweg gingen, Clie, TRGpro und Palm m505 waren doch alle irgendwie Handcomputer. Zudem boten alle identische APIs.

Spätestens seitdem Amazon in den Mobilmarkt eingestiegen ist, ändert sich das gravierend: Ein Fire-OS-Gerät unterscheidet sich in vielen Bereichen stark vom normalem Android. Waren die APIs für SD-Kartenzugriff und/oder virtuelle Graffitiflächen unter Palm OS "Randgruppenvorstellungen", unterscheiden sich heutige Stores massiv. Wer einmal eine komplette IAP-Implementierung (In-App Purchase) oder einen Kartenrenderer ausgetauscht hat, weiß, was gemeint ist.

Besonders kritisch ist in dem Zusammenhang die Vielzahl von Store-Varianten: Wer für jeden kleinen Vertriebskanal von Hand eine neue Applikation kompilieren muss, kann vom "Long Tail"-Effekt nur wenig profitieren und wird bald gewisse Channels nicht mehr bedienen.

Auch wenn das Ignorieren von Amazon schon vor einiger Zeit keine vernünftige Vorgehensweise mehr war, wirklich kritisch wird die Fragmentierung durch das Aufkommen von Android TV, Android Wear und Android Things. Waren Tablet und Telefon in der Bedienintention irgendwie verwandt, expandiert Android-Code nun in eine komplett andere Welt. In der Anfangszeit der Mobile-Industrie warnte Palm seine Entwicklerschaft permanent davor, GUI-Designs und Bedienkonzepte eins zu eins vom Desktop zu übernehmen.

Im Fall von Smart TVs ist das noch kritischer – eine österreichische Bank lieferte vor einigen Jahren mit einem Kinect-basierten Aktien-Analysewerkzeug ein klassisches Antipattern. Aus technischer Sicht war die Idee wunderbar: Mit Kinect und 50 Zoll-Fernseher lassen sich Kurse nun mal großartig darstellen. Das praktische Problem war, dass Passanten so Informationen über das Depot des gerade mit dem System interagierenden Individuums sammeln konnten.

Für Entwickler stellt sich an der Stelle eine besondere Herausforderung: Die schon aus Kostengründen gewünschte Wiederverwendung von Code setzt eine bisher nie dagewesene Menge an Mitdenken voraus. Das Reduzieren des Funktionsumfangs macht auch an anderer Stelle Sinn. So würde die Smartwatch-Version nicht davon profitieren, wenn sie die Chartansicht der Uhrenversion mit 50 verschiedenen Indikatoren "zumüllt" – Abbildung 1 gilt auch mehr als zehn Jahre nach ihrem Erscheinen.

Feature-Liste versus User Experience: Weniger kann mehr sein (Abb. 1) (Bild: PalmSource)

Kurz gefasst: War Gejaule über Fragmentierung bisher ein Problem mangelnden technischen Verständnisses, haben Entwickler nun wirklich einen Grund zur Sorge. Das aus kommerzieller Sicht sinnvolle Recyclen von Code setzt feingranulare Einstellungsmöglichkeiten voraus. Wer sie von Hand implementieren muss, hat mit der Abbildung aller Möglichkeiten einige hundert Stunden zu tun.

Ohne technische Unterstützung ist das ein Prozess, der in der Praxis eher früher als später aufgegeben wird. Bisher war die Automatisierung dieser Aufgabe großen Unternehmen vorbehalten – das Buildsystem Gradle wurde entwickelt, um dieses Problem zu vermeiden.

Das Flavor-Prinzip

Der Umstieg von Eclipse auf Android Studio vor einigen Jahren sorgte in der Entwicklerschaft für Unruhe: Gradle im Herzen der Entwicklungsumgebung ist und bleibt eine eigenwillige Angelegenheit, die GUI-verwöhnte Programmierer an Makefiles und andere Bösartigkeiten erinnert. Wer dem System etwas Zeit spendiert, stellt aber bald fest, dass die Angst unbegründet ist. Gradle basiert auf einem als Flavor bezeichneten Konzept, das in Abbildung 2 grafisch beschrieben ist.

Ein Flavor pro Store ist ein guter Anfang (Abb. 2).

Applikationen liegen als eine Kombination von Geschmacksrichtungen vor, die verschiedene Funktionsumfänge aufweisen und bei der Kompilierung aus unterschiedlichen Paketgruppen zusammengestellt werden. Zudem gibt es normalerweise zumindest eine Debug- und eine Release-Variante – deswegen kann die Menge der Kompilate im Laufe der Zeit stark anwachsen.

Zum Überwinden von Startschwierigkeiten möchte der Autor an der Stelle durch ein komplettes Beispiel führen, das einige Besonderheiten des Gradle-Buildsystems demonstriert. Begonnen sei mit der Generierung eines gewöhnlichen Projektskeletts auf Basis einer nicht allzu veralteten Version von Android. Entwickler öffnen im nächsten Schritt die zum Modul app gehörende build.gradle-Datei. Die relevanten Teile sieht man im folgenden Beispiel:

android {
compileSdkVersion 24
. . .
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
}

Auffällig ist, dass schon an der Stelle mehrere Kompilate vorliegen – die von Haus aus angelegte Version der Datei legt nämlich sowohl ein Release- als auch ein Debug-Kompilat fest. Für die Aufgabe hier – also die Nutzung des vollwertigen Flavor-Subsystems – ist stattdessen ein weiteres Attribut erforderlich, dass sich wie im folgenden Beispiel präsentiert:

android {
. . .
productFlavors {
forAmazon {
applicationId "com.tamoggemon.flavortest.amazon"
versionName "1.0-amz"
}
forPlay {
applicationId "com.tamoggemon.flavortest.play"
versionName "1.0-ply"
}
}
}

Das gezeigte Snippet legt zwei Flavors namens forAmazon und forPlay an. Beide bekommen je einen eigenen Versionsnamen und eine eigene Applikations-ID zugewiesen: Das Zuweisen einer eigenen ID wäre streng genommen gar nicht notwendig, ermöglicht aber ein interessantes Experiment mit dem Manifest-Merger.

Nach dem Speichern der neuen Dateiversion blendet Android Studio automatisch ein gelbes Banner ein, das zum erneuten Synchronisieren des Gesamtprojekts auffordert. Um das Buildsystem zur Analyse des Projekts zu animieren und dabei die notwendigen neuen Ordner und Dateien automatisiert anzulegen, muss man den Banner anklicken. Nach der erneuten Synchronisation klicken Entwickler auf Build | Select Build Variant, um das in der Abbildung gezeigte Fenster auf den Bildschirm zu holen. Das Feld Build Variant erlaubt dabei die Auswahl der Konfigurationskombination der Flavor-Kompilate, die beim Anklicken von Play oder Debug erstellt werden soll.

Dieses Fenster hilft Entwicklern bei der Handhabung mehrerer Flavors (Abb. 3).

Mit Android vertraute Entwickler wundern sich an der Stelle mitunter über folgende Passage in der Manifestdatei:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tamoggemon.flavortest">

Die hier festgelegte Package-ID ist unter Gradle für die Kompilierung selbst relevant. Sie beschreibt nur den Namespace, in dem die diversen Ressourcendateien abgelegt werden. Durch das Konstantsetzen dieses Werts ist sichergestellt, dass alle Code-Behind-Klassen ihre Zielelemente auch bei Änderungen des Gesamtpaketnamens finden können.