Ulm in 30 Sekunden

Für besondere Internetseiten und Portallösungen experimentieren wir immer wieder mit den unterschiedlichsten und manchmal aus sehr außergewöhnlichen Dingen. Dieses Mal mit einer vorprogrammierten Drohne, die handgesteuert „Ulm in 30 Sekunden“ zeigen soll. Die Geschwindigkeit des Videos wurde nicht bearbeitet, wir haben lediglich einige Farben angepasst. Als Kamera kam eine GoPro Hero 3 zum Einsatz, welche mit einem elektronischen Bildstabilisator und einer kardanischen Aufhängung direkt an der Drohne befestigt wurde. Das Video hat sich innerhalb kurzester Zeit über die Solzialen Netzwerke verbreitet und nach 2 Tagen bereits über 10.000 Views. Aber seht selbst:

In Unsere Projekte veröffentlicht

OKHttp für Android

Ein Klassiker der Android Entwicklung, mit dem man immer wieder konfrontiert wird, sind elegante und gut gemachte HTTP Requests, vor allem seit diese ab Android 4 in der synchronen Variante mit Exceptions um sich werfen.

Lange haben wir intern eine selbst entwickelte, asynchrone HTTP Bibliothek verwaltet, die zuerst auf die Apache HttpGet/Post etc. Methoden gesetzt und später mit HTTPUrlConnection gearbeitet hat. Aber richtig zufrieden waren wir damit nie, vor allem da sich die Anforderungen an diese oft von Projekt zu Projekt geändert haben.

Für die letzten Projekte nutzen wir verstärkt OKHttp, eine HTTP Library von Square, die uns in fast allen Belangen überzeugt hat.

Ein Beispiel für einen asynchronen Requests ist schnell zusammengebaut:

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
    .url("http://api.cortex-media.de/data/foo/1337")
    .build();

client.newCall(request).enqueue(new Callback() {
  @Override
  public void onFailure(Request request, Throwable throwable) {
    throwable.printStackTrace();
  }

  @Override
  public void onResponse(Response response) throws IOException {
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    String body = response.body().string();
    // do something with the body.
  }
});

Einfach den Client initialisieren, Request bauen und Call via enqueue() asynchron ausführen. Die Antwort wird dann im Callback behandelt, je nachdem ob der Request erfolgreich war (onResponse) oder nicht (onFailure).

Ein synchroner Request ist noch einfacher, man ersetze einfach die newCall()-Zeile durch folgende und verzichte auf den Callback:

Response response = client.newCall(request).execute();

Der Response ist dann direkt im entsprechenden Objekt gespeichert. Aufpassen muss man hier nur, dass man das nicht im UI Thread macht, sonst fängt man sich eine Exception ein.

Authentication

Will oder muss man seine HTTP Requests mit HTTP Authentication versehen, was wir gerne bei HTTPS-basierten APIs der schlichten Einfachheit wegen tun, setzt man einfach die entsprechenden Header mit den Ergebnis der Credentials-Klasse:

String credential = Credentials.basic("matthias", "extremely-secr3t");
Request request = new Request.Builder()
    .url("http://api.cortex-media.de/data/foo/1337")
    .header("Authorization", credential)
    .build();
 ...

Bisher sind wir wirklich sehr gut mit dieser Library gefahren. Solider Code, alle wichtigen Features dabei, SNI kompatibel (was gerade für uns sehr wichtig war) sowie aktive (Weiter-)Entwicklung war genau dass, was wir gesucht haben. Danke, Square!

In Technischer Hintergrund veröffentlicht | Getaggt: ,

Cortex Media Push-Server und die Frag-Mutti Apps

1402518737_bubbles

Für unsere Smartphone-Apps für Android und iOS benötigten wir schon relativ früh einen eigenen Push-Server, um diverse Nachrichten und Statusmessages direkt in die Apps pushen zu können. Die damals vorhandenen kommerziellen und Open-Source Angebote haben uns nicht überzeugt, so dass wir einen eigenen Push-Server entwickelt haben.

Der Push-Server

Wir haben uns für ein Konzept entschieden, das beide für uns wichtigen Push-Dienste vereint: Den Apple Push Notification Service (APNs) sowie sowohl das Google Cloud to Device Messaging Framework (C2DM) und als auch später den Google Cloud Messaging (GCM) Dienst. Der Push-Server empfängt über die entsprechenden APIs unserer Apps die Nachrichten und pusht, je nach Quelle, die Nachrichten weiter an die von Google und Apple angebotenen Schnittstellen. Hierbei kümmert sich der Server um ein logisches Buffering der Push-Messages, um beispielsweise bei einem Newsletter die Schnittstellen übermäßig zu beanspruchen. Um auch später für andere Pushes-Services bereit zu sein (vielleicht doch nochmal Windows Phone?), lässt sich der Server auch um Plugins erweitern, die andere Schnittstellen ansprechen können.

Anbindung der APIs

Für die Kommunikation mit dem Push-Server haben wir uns bewusst für eine direkte Socketverbindung zum Server entschieden. Dadurch erreichen wir vor allem bei sehr hohem Traffic oder der massenhaften Versendung von Pushes (kein Spam, dazu unten mehr) einen höheren Durchsatz als über eine HTTP-API oder ähnliches. Und da unsere APIs in der Regeln direkt mit dem Server sprechen können, und der Server auch nicht von externen Usern oder Diensten angesprochen wird, war dies die beste Alternative.

Der Server selbst verarbeitet die ankommenden Pushes über einen internen Buffer, der diesen nach und nach und je nach Schnittstelle abarbeitet. So kann die Anlieferung der Pushes von den APIs ohne Verzögerung geschehen und der Server kümmert sich asynchron um den Versand. Die Erfahrung zeigt, dass die Pushes oft schneller am Server ankommen wie die Service von Apple und Google diese entgegen nehmen, auch wenn diese Schnittstellen in der Regel sehr schnell reagieren. Falls ein Push nicht zugestellt werden kann, kann dieser auch wieder, je nach Response vom APNs oder GCM, zurück in den Buffer gelegt werden.

Die Frag-Mutti App

Die iOS und Android App für Frag-Mutti, die wir 2012 für Frag-Mutti.de entwickelt haben, nutzt unseren Push-Server zur Versendung diverser Nachrichten direkt an die Apps. Darunter z.B. praktische Tipps zu Pfingsten oder den Tipp der Woche.

Die Apps sind aktuell auf knapp 200.000 Geräten installiert (Stand Mitte 2014) und in etwa die Hälfte aller User haben die Push-Nachrichten aktiviert. Wenn Frag-Mutti Pushes verschickt, geschieht dies in der Regel gesammelt innerhalb eines kurzen Zeitfensters. Genau hier hat der oben beschrieben Buffer seine Stärken. Wenn Frag-Mutti mehrere 10.000 Pushes auf einmal anliefert, legt der Push-Server diese in den internen Buffer und arbeitet einen nach dem anderen ab. So kann der Client seine Daten in kurzer Zeit versenden, ohne weiter auf den Server und die dahinter liegenden Apple- und Google APIs zu achten.

In Technischer Hintergrund, Unsere Projekte veröffentlicht

Mehrsprachigkeit mit PHP und Smarty

In vielen unserer Projekte ist für den Kunden Mehrsprachigkeit ein sehr wichtiges Thema. Sei es bei einer App, die in verschiedensprachigen AppStores veröffentlicht werden soll, bei einer Intranet Anwendung, die für Zweigstellen in verschiedenen Ländern bereitgestellt wird oder bei der neuen Firmenhomepage, die in mehreren Sprachen abrufbar sein soll.

Während manche unserer eingesetzten Frameworks & CMS-Systeme von Haus aus eine sehr gute Unterstützung von Mehrsprachigkeit mitbringen (z.B. die App-Entwicklungsumgebungen von Android und iOS), gilt es bei nativen PHP Projekten selbst für eine sinnvolle Integration verschiedener Sprachen zu Sorgen.
 Diese Integration von Mehrsprachigkeit in einem Software-Projekt erfordert eine gute Planung und konsequente Umsetzung des Features von Beginn an. Nur so lässt sich vermeiden, dass man am Ende der Entwicklung eine Codebasis steht, bei der die Mehrsprachigkeit unwartbar und tief im Code versteckt ist.
Am Beispiel unseres oft eingesetzten hauseigenen PHP Frameworks möchte ich vorstellen, wie wir die Mehrsprachigkeit sinnvoll in das bestehende System integriert haben.

Mehrsprachigkeit mit PHP

Der naive Implementierungsansatz ist es, die verschiedenen Sprachen per switch oder if-else Konstrukte direkt bei der jeweiligen Codestelle abzufragen und entsprechend auszugeben:

 if($config::getCurrentLanguage() == Config::ENGLISH) {
     echo "Hello";
 } if($config::getCurrentLanguage() == Config::SPANISH) 
     echo "Ola";
 } else {
     echo "Hallo";
 }

Dieser Ansatz hat aber einige gravierende Nachteile:

  • Keine zentrale Stelle zum Editieren des Texts
  • Hinzufügen einer neuen Sprache erfordert, dass alle Stellen im Code gefunden und angefasst werden
  • Die Programmlogik wird durch eine große Menge von „unnötigen“ if-else Konstrukten aufgebläht und der Code wird immer schwerer wartbar

Das grundlegende Ziel der Implementierung der Mehrsprachigkeit sollte deshalb eine gekonnte Trennung der Codebasis und der tatsächlichen Übersetzung sein. PHP bringt mit der Unterstützung von GNU gettext schon eine relativ elegante Möglichkeit mit, diese Trennung vorzunehmen.

Um gettext zu benutzen, werden alle Textausgaben innerhalb des Quellcodes in einen gettext Aufruf gekapselt:

    echo "Das ist ein toller Tag!";

wird zu

    echo gettext("Das ist ein toller Tag!");

    /*Alternativ kann auch _ statt gettext verwendet werden*/
    echo _("Das ist ein toller Tag!");

Beim Aufruf schaut gettext nun in den bereitgestellten Übersetzungsdateien nach, ob eine entsprechende Übersetzung vorhanden ist. Falls ja, wird der übersetzte Text ausgegeben, ansonsten der Original-String.

Die Übersetzungsdateien sind sogenannte .po (Portable Object) Dateien, welche folgendermaßen aufgebaut sind:

 msgid "Das ist ein toller Tag!"
 msgstr "It's a wonderful day!"

msgid ist dabei der eindeutige Identifikator des zu übersetzenden Strings (Es werden statt der sprechenden Standardübersetzung auch gern abstrakte Identifier wie WELCOME_TO_PAGE_GOOD_DAY verwendet. Der Programmierer muss selbst entscheiden, ob es sinnvoller ist, dass diese abstrakten Identifier oder der Text in der Ursprungssprache zurück gegeben werden, sollte keine Übersetzung gefunden werden.).

Hinter dem Schlüsselwort msgstr folgt nun die jeweilige Übersetzung für die unterstützten Sprachen. Es muss für jede unterstützte Sprache ein eigene solche .po angelegt werden. Diese Dateien werden danach mit dem Konsolenkommando msgfmt in binäre .mo (Machine Objekt) Dateien umgewandelt.

PHP entscheidet dann auf Basis der gesetzten Locale welche Übersetzung gebraucht wird und versucht das passende .mo zu finden und den entsprechenden String auszulesen.

Unterstützung der Template Engine Smarty

Unser selbstentwickeltes PHP-Framework setzt auf eine konsequente Trennung von Darstellung und Logik. Wir setzen deshalb die Template Engine Smarty ein, um diese Trennung zu erreichen. Smarty benutzt dabei zur Darstellung Template Dateien (HTML angereichert um Steuerungsanweisungen), welche durch eine korrespondiere PHP Datei befüllt werden.

 
 /*Beispiel eines Smarty Templates, Steuerungsanweisungen werden durch { } geskapselt*/
 <table>
 {foreach $persons as $person}
     <tr>
         <td>{$person->getName()}</td>
     <tr>
 </table>

Da der Text nun in diesem Template File und nicht mehr in PHP selbst stehen, müssen wir auf ein Smarty Plugin zurückgreifen, welches die gettext-Abfragen übernimmt. Nach intensiver Suchen und ausführlichen Tests hat sich das Plugin smarty3-i18n als beste Alternative heraus kristallisiert.

Eine einfache gettext-Anweisung sieht mit diesem Plugin in Smarty wie folgt aus:

 {t}Bitte geben Sie Ihren Namen ein:{/t}

Übergabe von Variablen

Oft ist der Text bei Anwendungen nicht komplett statisch, sondern abhängig vom aktuellen Zustand der Anwendung. Ein einfaches Beispiel dafür ist eine Willkommensnachricht für den User nach dem Login, bei dem der Benutzer mit seinem Namen begrüßt wird. Es ist dabei nötig, den Text in den Übersetzungsdateien mit dem Namen des Users aus der Datenbank anzureichern.

Dazu ist es möglich, Ersetzungsvariablen innerhalb der Übersetzungen zu definieren. Im unseren einfachen Beispiel würde das so aussehen:

  /*Die po-Datei*/
 msgid "Willkommen auf der Webseite, %1 %2"
 msgstr "Welcome to the website, %1 %2"
   /*Smarty Template, die Variable $user wird aus PHP gefüllt*/
   {t firstname=$user->getFirstname() lastname=$user->getLastname()}Willkommen auf der Webseite, %1 %2{/t} 

Bei der Ausgabe des Smarty Templates wird dann automatisch der Text um den Namen des Benutzers ergänzt und ausgegeben. Die Bezeichnung der Variablen ist dabei egal, es zählt bei der späteren Ersetzung nur die Reihenfolge mit der die Variablen übergeben werden.

In Unsere Projekte veröffentlicht

Testphase Bilderupload unter http://upic.cc

Das mit den Bilderuploads im Web ist so eine Sache. Im Zeitalter von Social Media und dem damit verbundenen Drang zur Selbstdarstellung, ist man es gewohnt, in einfacher Form Bilder ins Web hochzuladen. Problematiken zum Datenschutz oder auch Urheberrechtsverletzungen erscheinen eher zweitrangig. Der Upload muss einfach nur gut funktionieren und meine Freunde bzw. die Internetgemeinde muss (von mir) begeistert sein. Was aber machen die meisten Anbieter von Bilderuploads? Entweder werden die Bilder sehr komprimiert und verkleinert (siehe Dienste wie Facebook, WhatsApp, usw.) oder die Darstellung der Bilder ist mit Werbung und anderen störenden Elementen übersäht (siehe quasi alle anderen Image-Hostern). Was aber, wenn ich z.B. als Fotograf einfach mal kurz ein Bild meinem Kunden zeigen möchte, ohne Werbung, ohne große Datenschutzbedenken, vielmehr in Top Qualität?

Da wir des Öfteren derartige Anfragen bekommen haben, wurde beschlossen, ein kleines Projekt namens http://upic.cc zu starten und einfach mal auf unseren freien Serverkapazitäten anzubieten — selbstverständlich kostenlos! Folgende Funtionalitäten leistet upic.cc:

  • Ein Benutzer zieht einfach ein JPG-Bild mit der Maus ins Browserfenster oder wählt auf seinem Handy ein Bild aus
  • Nun findet eine clientseitige Komprimierung und evtl. Verkleinerung (wobei wir hier von einer „Verkleinerung“ auf 3200 Pixel sprechen) statt, um wichtige Bandbreite zu sparen
  • Der Uploadprozess wird mit einer Progress-Bar angezeigt. Hierfür liefert der Server immer eine präzise Rückmeldung zu den übertragenen Daten
  • Das fertige Bild wird im Vollbild dargestellt und kann nun über einen privaten Link geteilt werden
  • Nach 10 Tagen löschen wir das Bild, so bleibt es auch nicht ewig im Netz weil man es vielleicht längst vergessen hat. Außerdem möchte man ja eh nur „kurz das Bild zeigen“
  • Eine Übersicht über hochgeladene Bilder findet sich dann weiterhin auf der Startseite
  • Das ganze auf einer „coolen Domain“

Klingt das gut? Einfach mal ausprobieren: http://upic.cc

In Unsere Projekte veröffentlicht

Asynchrone Codeexecution beim App-Update

Hintergrund

Für ein Kundenprojekt benötigten wir eine Möglichkeit, direkt nach einem Update der Android App via Google Play, Code innerhalb der App auszuführen, ohne dass zuvor die App vom User manuell gestartet wird.

Genauer ging es um die Notwendigkeit, einen GCM Subscribe Call an die Google GCM API zu schicken. Und das direkt nach dem Update der App, da wir nicht sicher gehen konnten, dass die User die App direkt oder auch in näherer Zukunft öffnen werden. Trotzdem sollte die App sich auf GCM Pushes registrieren.

Für Probleme diese Art sendet Android nach dem Update einer App einen Broadcast mit den entsprechenden Infos darüber, welches App upgedated wurde. Wir können auf diese Events hören und entsprechend reagieren.

Auf den Broadcast hören

Im AndroidManifest.xml definieren wir einen <receiver>, der auf die android.intent.action.PACKAGE_REPLACED Action horcht:

<receiver android:name="de.cortex_media.gcm.GCMBootstrap">
  <intent-filter>
    <action android:name="android.intent.action.PACKAGE_REPLACED" />
    <data android:scheme="package" android:path="de.cortex_media.gcm" />
  </intent-filter>
</receiver>

Hiermit können wir alle Events dieser Art im Broadcast Receiver GCMBootstrap weiter verarbeiten.

Broadcast Receiver

Da alle Update-Events des Systems nun hier landen, müssen wir auf den entsprechenden Packagename filtern, den wir an dieser Stelle beobachten wollen:

public class GCMBootstrap extends WakefulBroadcastReceiver {
  @Override  
  public void onReceive(Context context, Intent intent) {

    if (intent != null) {
      String data = intent.getDataString();
      String action = intent.getAction();

        if (data.contains("de.cortex_media.gcm")) {    		
          // Your code ...
          // We are doing the GCM Subscribe here.
        } 
    } 
  }
}

Damit die App bei einem entsprechenden Broadcast auch zuverlässig aufwacht und den obigen Code ausführt, bietet es sich an, einen WakefulBroadcastReceiver zu verwenden. Hier ist nicht viel mehr zu tun, als von der entsprechenden Klasse zu erben (siehe oben) und die passende Permission zu verwenden:

<uses-permission android:name="android.permission.WAKE_LOCK" />

Der Code, den wir an einer solchen Stelle ausführen, sollte im Allgemeinen nicht zuviel Arbeit leisten. Andere Apps laufen eventuell parallel, das System selbst ist auch selten ohne Arbeit und wenn die eigene App dann verdeckt viel CPU Leistung zieht, sind die User selten begeistert.
Eventuell bietet sich an dieser Stelle auch ein eigener Service an, was aber eher ein Thema für den nächsten Blogeintrag wäre.

In Technischer Hintergrund veröffentlicht | Getaggt:

Cortex Tickets – Online-Ticketvorverkauf für jedermann

Als die Cortex Media GmbH startete, war ein großes Ziel von uns, neben dem Anbieten von individuellen Lösungen im Bereich Webentwicklung, Server und Internetapplikationen auch der Vertrieb von eigenen Produkten. Wir begannen deshalb relativ zügig mit einigen Brainstormings, was denn dieses erste eigene Produkte sein könnte. Auf Grund von persönlichen Erfahrungen beim Veranstalten von kleinen und größeren Theater-, Musical- und Konzertveranstaltung war die Idee schnell geboren:

Wir wollten einen kostengünstigen Online-Ticketverkauf als Alternative für kleine und mittelgroße Veranstalter schaffen, denen die Konditionen und Preise großer Anbieter nicht zusagen und die von der oftmals unnötigen Komplexität abgeschreckt sind. Auch ein Name war schnell geboren und so starteten wir im Februar 2010 mit der Entwicklung unseres eigenen Online-Ticketverkauf-Portals cortex-tickets.de.

Die Startseite unseres Portals www.cortex-tickets.de

Die Startseite unseres Portals www.cortex-tickets.de

Oberste Maxime bei der Entwicklung waren natürlich die Grundsätze, die wir auch bei all unseren Kundenprojekten anlegen: Hohe Sicherheit, ausgezeichnete Performance, Hochverfügbarkeit, Mobilität, Innovation und die Einbeziehung neuster Technologien. Außerdem war uns ein besonderes Anliegen, eine faire Preisstruktur und eine sehr einfache und intuitive Bedienoberfläche zu schaffen.

Neben der Webentwicklung des Portals bot das Projekt noch weitere spannende Herausforderungen, die viele der Themenbereiche abdecken, in denen die Cortex Media GmbH auch als Dienstleister Auftritt:

  • Der Benutzer druckt die gekauften Tickets in unserem System selbst bei sich zu Hause aus. Dafür wurde ein Modul entwickelt, das eine beliebige Anzahl von Tickets mit Bar- und QR-Codes als PDF generiert und diese dem Benutzer per E-Mail schickt bzw. zum Download bereitstellt.
  • Die Webplattform speichert Kundendaten und Bezahlinformationen. Diese Daten sind natürlich sensibler als die meisten Daten, die gewöhnliche Webseiten speichern. Wir haben deshalb hier besonderen Augenmerk auf Datenschutz und Datensicherheit gelegt.
  • Natürlich müssen auch zahlreiche Bezahlsysteme sinnvoll und sicher in die Webseite integriert werden. Cortex Tickets unterstützt inzwischen die reibungslose Zahlung mit Visa & Mastercard, Giropay und per Vorkasse.
  • Um den Arbeitsaufwand für unsere Mitarbeiter möglichst gering zu halten, mussten auch viele automatisierte Prozesse entwickelt werden, zum Beispiel die Auszahlung an den Veranstalter oder die Abgleichung der Vorkassenzahlungen. Cortex Tickets ist in der Lage viele dieser Prozesse völlig ohne menschliches Eingreifen automatisiert durchführen.
Die Verkaufsseite eines Beispielevents

Die Verkaufsseite eines Beispielevents

Ein besonders Augenmerk wollten wir auch auf möglichst einfache Einlasskontrollen legen, denn was nützt einem Veranstalter der beste Onlinevorverkauf, wenn später am Einlass Chaos entsteht. Wir haben dieses Problem durch eine Kombination aus klassischer Einfachheit und moderner Innovation gelöst:

Ganz klassisch kann der Veranstalter sich bei uns eine Gästeliste ausdrucken und die einzelnen Besucher abhaken. Er hat jedoch auch die Möglichkeit über den auf dem Ticket integrierten Barcode eine Einlasskontrolle mit einem Barcode-Scanner und unserer modernen Multiplattform-Software durchzuführen.

Als besondere Innovation bietet Cortex Tickets auch die Einlasskontrolle per iPhone und Android an. Hier benötigt der Veranstalter nur die kostenlose Cortex Tickets App und kann mit der integrierten Kamera des eigenen Smartphones die Kontrolle vornehmen.

Einlasskontrolle per Barcodescanner und Cortex Tickets Software

Einlasskontrolle per Barcodescanner und Cortex Tickets Software

Einlasskontrolle per iPad

Einlasskontrolle per iPad

Heute ist Cortex Tickets mit einem Jahresumsatz von über 100.000 € und mehreren hundert Events pro Woche eine etablierte und beliebte Ticketplattform, die sich mit den fairen und günstigen Preisen hinter keinem Konkurrenten verstecken muss.

In Unsere Projekte veröffentlicht

Android Event Bus for fun and profit

Was ist ein Event Bus und warum ist er so praktisch?

Eine typische Android-App besteht in der Regel aus einer nicht kleinen Menge an Activities, Fragments und Services. Diese Komponenten der App müssen in vielen Fällen miteinander kommunizieren, um sich zum Beispiel gegenseitig mit Events und Updates zu versorgen.

Ein einfaches Beispiel dafür sind Push-Nachrichten. Angenommen die App empfängt über einen BroadcastReceiver einen Push und liefert diesen in einen Hintergrundprozess der App weiter (in der Regel ein IntentService). Jetzt steht man vor der Aufgabe, wie man von dieser Stelle aus genau die Teile der App informiert, für die der Empfang des Pushes von Bedeutung ist. Zum Beispiel weil man im User Interface durch ein grafisches Element andeuten will, dass eine neue Nachricht angekommen ist oder man den Datenbank-Layer auffordern möchste, die Daten des Pushes zu speichern.

Eine Möglichkeit dieses Problem zu lösen sind normale Referenzen auf die jeweiligen Fragments oder Activities. Aber will man das? Eher nicht. Man würde damit feste Beziehungen zwischen diesen Komponenten eingehen und ein späteter Austausch von Teilen der App wäre mehr als umständlich. Von der Null-Pointer-Gefahr mal ganz zu schweigen.

Android ohne Event Bus

Android ohne Event Bus und mit festen Beziehungen zwischen den Komponenten

Android selbst bringt von Haus aus ein Event-System mit, das als solches vielleicht zunächst gar nicht zu erkennen ist: Android bietet die Möglichkeit Activities über Intent-Extras und Fragments über Bundles mit den entsprechenden Daten zu starten. Aber auch hier ist man nicht wirklich flexibel: Man muss auch diese Activities & Fragments wieder fest verdrahten und ist auch etwas eingeschränkt bei der Wahl der Datentypen.

Und genau an dieser Stelle kommt der Event Bus ins Spiel. Ein Event Bus ist quasi nichts anderes als ein Kommunikationskanal, in den man zum einen reinhören und zum anderen reinrufen kann. Am Beispiel von oben erklärt: Eine Komponente der App, die wartet bis ein Push ankommt, horcht in den Bus hinein. Der Receiver, der den Push empfängt, ruft bei Empfang die jeweiligen Infos in den Bus. Sprich wir haben eine klassische Publish/Subscriber-Architektur, ähnlich den Listenern, aber generischer zu nutzen.

Android mit Event Bus

Android mit Event Bus. Loose Coupling und saubere interne Kommunikation

GreenRobot, Otto und Guava

Für Android gibt es drei Frameworks um mit einem Event Bus zu arbeiten. Guava von Google ist ein EventBus für Java, der auch, aber nicht nur, für Android genutzt werden kann. Der GreenRobot EventBus und Square’s Otto sind speziell für Android angepasste Variante von EventBus Systemen.

Die Frage, ob man Otto oder den GreenRobot einsetzt, ist teilweise mehr ein Glaubenskrieg als ein faktisches Abwegen. GreenRobot wirbt zwar mit vielen Features, die Otto nicht unterstützt, teilweise sind diese aber auch aus Gründen der Einfachheit nicht in Otto enthalten (z.B. lässt sich die Delivery der Events von Background Threads in den Main Thread mit recht wenig Code auch hier verwirklichen).

Was man nun verwendet, ist unserer Meinung nach Geschmackssache. Wir arbeiten sehr gerne mit Otto, da die einfache Verwendung in Kombination mit dem guten Ruf, den die Android Bibliotheken von Square haben, uns immer wieder überzeugt haben. Ich bin mir aber relativ sicher, dass wir genauso mit Greenrobot glücklich geworden wären.

Beispiel: Ein EventBus mit Otto

Greifen wir das Beispiel von oben auf und implementieren beispielhaft einen EventBus, der in einem Fragment generierte Events an die darunter liegende Activity schickt.

Es bietet sich an, den EventBus in einem Singleton bereit zu stellen, um nicht bei jeder Verwendung das Objekt neu zu initialisieren.

public class CortexBus {
    private static Bus instance;	

    private CortexBus() {}

    public synchronized static Bus getInstance() {
        if (instance == null) {
            instance = new Bus();
        }
        return instance;		
    }
}

Im Fragment wollen wir die Events verschicken. Als Beispiel nehmen wir eine Kurznachricht, die dieses Fragment z.B. über eine HTTP Schnittstelle empfangen hat. Hier sind beliebig komplexe Events möglich, da jeder Event über eine eigenen Klasse (ein einfaches POJO) repräsentiert wird. Diese sieht in unserem Beispiel wie folgt aus:

public class MessageReceivedEvent {	
    public long messageId;
    public String message;

    public MessageReceivedEvent(long messageId, String message) {
        this.messageId = messageId;
        this.message = message;
    }
}

Diesen Event können wir nun über den Bus an alle registierten (siehe unten) Subscriber schicken, ohne diese direkt kennen zu müssen:

public class CardFragment extends Fragment {
    private CortexBus bus;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        bus = CortexBus.getInstance();

        // code, um die nachricht zu empfangen, usw.
        // long messageId = ...
        // String message = ...

       bus.post(new MessageReceivedEvent(messageId, message));

        // ... weiterer Code ...
    }
}

In der Activity wollen wir auf eingehende Events hören. Wir initialisieren den Bus über den Singleton und erstellen eine Methode mit der @Subscribe Annotation, die die jeweiligen Events empfängt. Welchen Event diese Methode empfängt, wird über die Methodensignatur und die Annotation entschieden. Der Name der Methode ist hierbei nicht wichtig und kann frei gewählt werden. Die Activity muss sich dazu im onResume() und onPause() jeweils an- bzw. abmelden, damit die Events überhaupt empfangen werden.

public class MainActivity extends FragmentActivity {
    private Bus bus;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        bus = CortexBus.getInstance();
    }

    @Override
    protected void onResume() {
        super.onResume();		
        bus.register(this);
    }		

    @Override
    protected void onPause() {
        super.onPause();
        bus.unregister(this);
    }

    @Subscribe 
    public void onPushReceived(PushReceivedEvent event) {
        // dein event handling passiert an dieser Stelle
        // ....
    }
}

Das ist alles, was für ein einfaches Event Bus System nötig ist. Man erreicht damit ein sehr angenehmes und stabiles Loose Coupling der Komponenten seiner App. Vor allem wenn die eigene Anwendung komplexer wird, kann man sich damit sehr viel Ärger und NPE-Schmerz ersparen.

In Technischer Hintergrund veröffentlicht | Getaggt: ,