© Excellent backgrounds/Shutterstock.com
Teil 1: Entwicklung einer App

Der fehlende Android-Guide


Die offizielle Android-Dokumentation ermöglicht einen einfachen Einstieg in die Entwicklung von Android-Apps. Open-Source-Frameworks oder Tools anderer Anbieter sind hier nicht mit einbezogen, ebenso fehlen Hinweise auf Design-Pattern, die unschöne Designentscheidungen im Android-SDK ausgleichen könnten. Dies führt häufig zu unsauberem und unnötigem Code sowie langsamen und fehleranfälligen Anwendungen. Genau hier soll diese Reihe einsetzen. Sie bildet eine Weiterführung der existierenden Android-Dokumentation.

Die offizielle Android-Dokumentation beschränkt sich hauptsächlich auf die native Entwicklung von An­droid-Apps mit den vom SDK zur Verfügung gestellten Mitteln. So wie in der Java-Entwicklung die eigene Anwendung mit externen Frameworks wie Spring, Hibernate oder dergleichen erweitert wird, gilt dies ebenso für die Entwicklung von Android-Apps. Häufig handelt es sich hierbei um andere Frameworks als die bereits bekannten. Diese Reihe richtet sich hauptsächlich an Java-Entwickler, die in die Android-Entwicklung eingestiegen sind, aber noch keine tieferen Erkenntnisse gesammelt haben. Die hier vorgestellten Inhalte zeigen wichtige Werkzeuge und Methoden auf, um von unstrukturierten Anwendungen zu gut strukturierten und leicht zu pflegenden Anwendungen zu kommen, und wie man diese leichtgewichtig testen und betreiben kann.

Die Beschreibungen erheben keinen Anspruch an Vollständigkeit, vielmehr beschreibe ich die Erkenntnisse, die bei uns innerhalb der Android-Entwicklung von entscheidender Bedeutung waren und uns die Entwicklung deutlich vereinfacht haben. Die Reihe teilt sich in drei Abschnitte, die zeitlich klassischerweise aufeinander folgen (Abb. 1):

  • Entwicklung der App

  • Testen der App

  • Betreiben der App

gruczel_androidentwicklung_1.tif_fmt1.jpgAbb. 1: Klassische Entwicklungszyklen

Der erste Abschnitt fokussiert auf die Entwicklung einer nativen Android-App. Ich möchte auf einige wichtige Elemente und Frameworks in der Entwicklung von nativen Android-Apps verweisen. Android-Code kann schnell zu Spaghetticode mutieren. Die hier vorgestellten Konzepte und Bibliotheken helfen bei der technisch sauberen Strukturierung der Anwendung und der schnellen Entwicklung. Ich werde deshalb diejenigen Bibliotheken und Pattern vorstellen, die mir am stärksten geholfen haben. In vielen Fällen gibt es andere Bibliotheken, die einen ähnlichen Zweck erfüllen, die Konzepte dahinter sind meist sehr ähnlich.

Im zweiten Teil werde ich einige Hinweise zum Testen geben, um mich im dritten Teil auf den Betrieb der Apps zu konzentrieren: Dieser Teil enthält Themen wie Monitoring, Tracking, und A/B-Tests.

Wie werde ich den ganzen Boilerplate-Code los?

Android-Code ist voller Boilerplate-Code. Unter Boilerplate-Code verstehe ich Code, der dem Zweck dient, das Framework zu verwenden – Code also, von dem man in einem perfekten Framework erwarten würde, dass er gar nicht nötig ist. Die gefühlt hohe Menge an derartigem Code bei der Android-Entwicklung hat verschiedene Ursachen. So müssen zum Beispiel die Lifecycles der Anwendung verwaltet werden, und es muss eine Verbindung zwischen deklarativen Beschreibungen in Form von XML (zum Beispiel Layouts) und Code hergestellt werden. Ebenso muss eine große Menge technischer Logik implementiert werden. Dieser Code ist nicht nur schädlich, weil er geschrieben werden muss und somit potenziell Fehler enthalten kann, sondern auch, weil er von der eigentlichen Geschäftslogik ablenkt und sich mit ihr leicht vermischt. Zudem kostet das Schreiben Zeit, die sinnvoller in die Entwicklung von fachlicher Logik investiert werden sollte.

Über Annotationen kann man hier viel Zeit und Code einsparen. Es gibt unzählige Frameworks dafür. Einige Frameworks bieten Annotationen, um zum Beispiel die Ausführung von Code explizit auf einen Background-Thread oder auf den UI-Thread zu verlegen, Rest Calls auszuführen, Scopes zu definieren, den Aufruf von Methoden an bestimmte Lifecycle-Phasen zu binden oder Daten mit dem Storage zu synchronisieren. Ich werde dies anhand von zwei Beispielen erläutern. Bei der Android-Entwicklung sollte man regelmäßig prüfen, ob nicht wesentliche Vereinfachungen möglich sind. Unter http://androidannotations.org bekommt man einen Eindruck davon, wie Code mit Boilerplate im Vergleich zu Code mit Annotationen aussehen kann.

Butterknife

Als erstes Beispiel möchte ich hier Butterknife [1] zeigen, da es extrem leicht einzusetzen ist und der Mehrwert sofort sichtbar wird. Hat man erst einmal ein Framework wie Butterknife eingesetzt, wird man schnell anfangen, viel selbstgeschriebenen Code mit Annotationen ersetzen zu wollen. Butterknife konzentriert sich auf das Entfernen von Boilerplate-Code, der durch die Bindung vom UI zur Logik entsteht. Butterknife bindet UI-Elemente und Interaktionen an Felder (View Injection) und Methoden (Listener Injection) mithilfe von Annotationen. Es generiert dazu entsprechenden Code innerhalb des Build-Prozesses, womit Überraschungen zur Laufzeit ausbleiben. Der Generierungsprozess wird dadurch nicht wesentlich komplizierter, da Android ohnehin Klassen generieren muss. So wird zum Beispiel die R-Datei mit den IDs für das UI bei jedem Kompiliervorgang generiert. Insofern ist dieser zusätzliche Schritt, der im Übrigen sehr schnell durchgeführt wird, kein Problem. Klassischerweise werden UI-Elemente mittels findViewById geholt. Dann werden sie gecastet und Listener werden programmatisch an die UI-Elemente gehängt. Mit weniger Code kommt man aus, wenn in der UI-Deklaration definiert wird, welche Methode bei der Interaktion mit einem Element aufgerufen werden soll. Es bleibt also die Wahl zwischen umständlichem Code oder einer unsauberen Trennung zwischen der UI-Deklaration und der Logik. Dank Butterknife hat man nun die Möglichkeit, dies mit ein paar Annotationen im Code zu erledigen. Das bedeutet, mit einer Annotation über einer Instanzvariablen kann bereits sichergestellt werden, dass die Instanzvariable mit der entsprechenden View gefüllt wird, nachdem die View aufgebaut wurde. Eine Annotation an einer Methode stellt sicher, dass bei einer Interaktion auf dem UI die entsprechende Methode ausgeführt wird. Man vergleiche Listing 1, das auf klassische Weise vorgeht, mit Listing 2, das Annotationen verwendet. Listing 1 holt sich die Elemente aus dem UI mit findViewById und hängt dann anonyme Listener an das Element. Hier sehen wir bereits viel überflüssigen Code. So ist zum einen die Definition von anonymen Listenern in Java, die kaum Logik ausführen, nicht besonders effizient, und zum anderen ist das Füllen von Feldern mittels findViewById unsauber. Es muss immer darauf geachtet werden, dass dieser Vorgang ausgeführt wird, bevor das Element das erste Mal verwendet wird.

Listing 1

public class ExampleActivity extends Activity { private Button button1; private Button button2; @Override protected void onCreate(Bundle savedInstanceState) { // ... button1 = (Button) findViewById(R.id.button1); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // ... } }); button2 = (Button) findViewById(R.id.button2); } }

In Listing 2 sehen wir, wie die gleiche Logik mit jeweils einer Annotation erledigt werden kann. Dabei stellt Butterknife nicht nur das Setzen der Felder sofort nach dem Aufbau der View sicher, sondern macht Interaktionen zu ei...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang