• Like

Android auf der Couch (Mobile Technology 2013)

  • 256 views
Uploaded on

NoSQL-Datenbanken erfreuen sich im Webumfeld zunehmender Beliebtheit, doch kann man sie auch sinnvoll auf mobilen Geräten einsetzen? Der erste Artikel „CouchDB mobile“ hat die NoSQL-Datenbank CouchDB …

NoSQL-Datenbanken erfreuen sich im Webumfeld zunehmender Beliebtheit, doch kann man sie auch sinnvoll auf mobilen Geräten einsetzen? Der erste Artikel „CouchDB mobile“ hat die NoSQL-Datenbank CouchDB im Zusammenspiel mit iOS beleuchtet. Dieser Artikel widmet sich dem Einsatz im Android-Umfeld.

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
256
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
4
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. CouchDB | ANDROID 035 red ile B mob chD – vo roid fla And Cou © iStockphoto.com/FreeSoulProduction © iStockphoto.com/-Mosquito- uf der Couch Android a NoSQL-Datenbanken erfreuen sich im Webumfeld zunehmender Beliebtheit, doch kann man sie auch sinnvoll auf mobilen Geräten einsetzen? Der erste Artikel „CouchDB mobile“ hat die NoSQL-Datenbank CouchDB im Zusammenspiel mit iOS beleuchtet. Dieser Artikel widmet sich dem Einsatz im Android-Umfeld. von Peter Friese und Stefan Reichert Die grundlegenden Konzepte und Eigenschaften von CouchDB sind im ersten Artikel bereits ausführlich beschrieben worden. An dieser Stelle soll ein kurzer Abriss genügen. CouchDB ist eine dokumentenorientierte NoSQL-Datenbank. Die Daten werden schemalos in Form von JSON-Dokumenten abgelegt, wobei die Kommuni- www.mobile360.de kation mit der Datenbank ausschließlich REST-basiert über HTTP stattfindet. Eines der wesentlichen Merkmale von CouchDB ist die Unterstützung einer robusten Replikation. Diese Fähigkeit macht den Einsatz gerade im mobilen Umfeld interessant, wie im ersten Artikel deutlich geworden ist. Das dort beschriebene Lauf-AppBeispiel soll in diesem Artikel für Android nachempfunden werden. FA-273, 2013 1 | 2013 Mobile Technology
  • 2. 036 ANDROID | CouchDB Recapture – die Architektur Zu Beginn wollen wir nochmal einen kurzen Blick auf die Architektur der App werfen (Abb. 1). Das Aufzeichnen der Wegpunkte einer gelaufenen Strecke soll neben dem iPhone auch mit einem Android-Smartphone möglich werden. Der GPS-Sensor des Smartphones dient dabei zur Erfassung der jeweils aktuellen Position des Läufers. Da wir beim Laufen nicht von einer permanent verfügbaren Internetverbindung ausgehen können (z. B. könnte die Netzabdeckung lückenhaft, bzw. das Smartphone aufgrund hoher Roaming-Gebühren im Ausland vollständig offline sein), müssen die GPS-Daten zunächst lokal gespeichert werden, bevor sie zu einem geeigneten Zeitpunkt mit einem Server – in unserem Fall mit einer CouchDB in der Cloud – synchronisiert werden können. Die Synchronisierung mit einer zentralen CouchDB-Instanz hat nicht nur den Grund, die Daten für eine spätere Auswertung verfügbar zu machen. Bei einer vorhandenen Internetverbindung und dadurch möglichen zeitnahen Updates wäre es durchaus denkbar, die aktuelle Position des Läufers und die seiner Kontrahenten während eines Wettkampfes auf einer großen Kartendarstellung anzuzeigen. Bereits im vorangegangenen Artikel haben wir dazu eine CouchDB-Instanz angelegt – entweder in der Cloud auf http:// iriscouch.com oder lokal auf dem eigenen Rechner (da der eigene Rechner üblicherweise nicht öffentlich sichtbar im Internet erreichbar ist, ist letztere Konstellation natürlich nur zu Entwicklungszwecken sinnvoll). Um eine möglichst transparente Synchronisierung zwischen den lokal auf dem Smartphone gespeicherten Daten und den in der Cloud gespeicherten Daten zu ermöglichen, setzen wir auch auf dem Client CouchDB ein. CouchDB verfügt über einen sehr einfach zu verwendenden Replikationsmechanismus, der sich ideal für unsere Zwecke eignet: Lokal abgelegte Daten werden bei Verfügung einer Internetverbindung mit der Gegenstelle (d.  h. der zentralen CouchDB-Instanz) abgeglichen. CouchDB geht dabei ressourcenschonend vor und sendet immer nur Deltas. Wenn viele Läufer die App einsetzen und somit sehr viele Datensätze (bzw. Dokumente) erzeugt werden, besteht prinzipiell die Möglichkeit, Listing 1: Herstellen der Verbindung zu einer lokalen TouchDB-Instanz ... // TouchDB Server erstellen String filesDir = getFilesDir().getAbsolutePath(); TDServer touchDBServer = new TDServer(filesDir); // Verbindung zu einer Datenbank aufbauen HttpClient httpClient = new TouchDBHttpClient(touchDBServer); StdCouchDbInstance couchDBInstance = new StdCouchDbInstance(httpClient); CouchDbConnector couchDBConnector = couchDBInstance.createConnector("couch25k", false); ... Mobile Technology 1 | 2013 dass es beim Synchronisieren zu Schlüsselkollisionen kommt. Dem entgeht CouchDB durch die Verwendung von UUIDs, sodass alle Schlüssel global eindeutig sind. Kollisionen, die sich ggf. durch gleichzeitiges Aktualisieren von ein und demselben Datensatz ergeben könnten, werden durch die Verwendung eines Revision Counters aufgelöst – es gewinnt zunächst immer der höchste Revision Counter. In unserem Fall kann es prinzipbedingt nicht zu Konflikten kommen, da einzig und allein die Smartphones neue Datensätze erzeugen und die Datensätze nachträglich nicht verändert werden. Die Synchronisation der Daten erfolgt asymmetrisch: Alle auf dem Smartphone erfassten Wegpunkte werden zur zentralen Instanz übertragen. Im Gegenzug werden allerdings nur diejenigen Daten von der zentralen Instanz an die jeweiligen Smartphones übertragen, die dem Benutzer zugeordnet sind. Dieser Mechanismus dient einerseits dem Datenschutz, andererseits verfügen Smartphones nicht über den notwendigen Speicherplatz, um eine komplette Replikation der zentralen Datenbank mit den kompletten Daten von potenziell Tausenden von Benutzern zu fassen. Der von CouchDB bereitgestellte Replikationsmechanismus [1] ist recht mächtig, dennoch einfach zu konfigurieren, und ermöglicht es uns, die Applikation ohne großen Aufwand offlinefähig zu machen. CouchDB auf Android In Teil 1 des Artikels haben wir beschrieben, dass CouchDB auf iOS durch die schnittstellenkompatible TouchDB-Library implementiert werden kann. Erfreulicherweise gibt es eine entsprechende Bibliothek auch für Android, sodass wir als Basis für diesen Artikel auf TouchDB for Android [2] zurückgreifen werden. Für den Einsatz von TouchDB sprechen die gleichen Argumente, die wir bereits im ersten Teil erwähnt haben: Footprint und möglichst kurze Startzeit der Bibliothek. Der Android-Client kommuniziert wie gewohnt über ein REST-API mit der lokalen Datenbank. Möglich wird dies analog zum iPhone-Port durch ein NSURLProtocol, TouchDB registriert das URL-Schema touchdb. TouchDB verwendet die auf dem Gerät zur Verfügung stehende SQLite-Datenbank. Die Dokumente werden als Blob unter der Dokument-ID gespeichert. Die in CouchDB üblicherweise in JavaScript geschriebenen MapReduce-Funktionen werden hier nativ in Java geschrieben. Alternativ kann man auch JavaScript verwenden, die Funktionen werden dann mithilfe von Rhino interpretiert und ausgeführt. Zusätzlich zu den Dokumenten werden die Ergebnisse der Map-Funktion einer View in der SQLite-Datenbank abgelegt. Das beschleunigt die Zugriffe auf die Map-Ergebnis-Daten für die Reduce-Funktion. Anschließend wird die MapFunktion jeweils für neue Dokumente ausgeführt. TouchDB-Views sind versioniert, die gespeicherten Ergebnisse hängen an der jeweiligen Version der View, die direkt im Code angegeben ist. Verändert sich die View, dann muss im Code die Versionsnummer erhöht www.mobile360.de
  • 3. CouchDB | ANDROID werden. Die bereits gespeicherten Ergebnisse sind damit ungültig und werden beim nächsten Start der TouchDB gelöscht und aktualisiert. Für die Verwendung der TouchDB verwenden wir die Ektorp-Bibliothek [3]. TouchDB liefert einen passenden Adapter, mit dem Ektorp mit ein paar wenigen Ausnahmen wie bei einer gewöhnlichen CouchDB verwendet werden. Dazu aber später mehr. Projektsetup Zunächst wollen wir uns anschauen, wie man ein Projekt mit TouchDB und Ektorp aufsetzen kann. Dazu wird die Eclipse-IDE und das dazu passende Android SDK verwendet. Folgende Schritte sind notwendig: 1. Neues, leeres Android-Projekt anlegen 2. Im Projektverzeichnis einen Ordner /libs/ anlegen, Bibliotheken dieses Ordners befinden sich automatisch im Klassenpfad der neuen Android-App 3. TouchDB-Android klonen: git clone git://github. com/couchbaselabs/TouchDB-Android.git 4. Die Projekte TouchDB-Android und TouchDBAndroid-Ektorp als Eclipse-Projekte in das IDE importieren 5. Kopieren des Inhalts der /libs/-Ordner beider TouchDB-Projekte in den neu angelegten /libs/-Ordner des leeren Android-Projekts 6. Kopieren der Binary-Java-Archive der TouchDBProjekte aus dem jeweiligen /bins/-Ordner (touchdbandroid.jar und touchdb-android-ektorp.jar) in den neu angelegten /libs/-Ordner des leeren AndroidProjekts (die Archive werden vom IDE automatisch erzeugt) Abb. 1: Architektur unserer Lauf-App CouchDB-Dokumente verwalten Für das Verwalten der Dokumente verwenden wir Ektorp, was das Arbeiten mit einer CouchDB vereinfacht. Es bietet ein Mapping eines JSON-Dokuments auf JavaObjekte eines passenden Typs. Für die Konvertierung wird intern die Jackson-Bibliothek [4] genutzt. Somit erübrigt sich das Arbeiten mit Key-Value-Paaren im Code, Tracken von GPS-Wegpunkten Das Tracken der GPS-Daten stellt den zentralen Anwendungsfall der Lauf-App dar. Die Aufzeichnung von GPS-Daten erfolgt unter Android wie folgt: Die Plattform stellt als SystemService ein Objekt der Klasse android.location.LocationManager bereit. Die Methode requestLocationUpdates(provider, minTime, minDistance, listener) bietet die Möglichkeit, einen LocationListener anzumelden. Bei Änderungen des Standorts innerhalb der definierten Parameter wird der registrierte Listener durch Aufruf der Methode onLocationChanged(location) benachrichtigt. Das Location-Objekt der Signatur beinhaltet die jeweils aktuelle Position des Geräts. Da das Ermitteln von Positionsdaten den Akku des Smartphones belastet, sollte der LocationListener also erst bei Bedarf registriert werden. Zudem sollte die Abfragefrequenz mithilfe der Parameter minTime und minDistance ökonomisch sinnvoll gewählt werden. Ablegen der Daten in der lokalen Datenbank Die Daten des Location-Objekts werden zunächst in einer lokalen CouchDB abgelegt. Dafür benötigen wir als Erstes eine Verbindung zur Datenbank (Listing 1). www.mobile360.de Anzeige Anzeige 037
  • 4. 038 ANDROID | CouchDB oder alle Läufe eines Users, über die gesamte Datenmenge durchführen zu können. Sollte ein Benutzer sowohl ein iPhone als auch ein Android Device verwenden, schließt dies natürlich auch eventuell auf dem iPhone getrackte Daten ein, die über die später vorgestellte Replikation auf das Android-Smartphone synchronisiert wurden. In Listing 2 ist zu sehen, dass die Klasse Trackpoint von der Klasse CouchDbDocument erbt. Damit kann mit einem Repository ein weiteres Ektorp-Feature genutzt werden. CouchDbDocuments können mithilfe eines Repositories verwaltet werden. Ein Repository kapselt die Zugriffe auf die Datenbank, die für das Arbeiten mit Objekten einer bestimmten Klasse notwendig sind. Die Basisklasse bietet von sich aus bereits die CRUD-Operationen an und kann um zusätzliche Views erweitert werden. Zunächst sehen wir uns aber das Speichern von Trackpoints an. Mit Repositories arbeiten Abb. 2: Übersicht der Läufe Abb. 3: Detailansicht eines Laufs die Datenstrukturen können intuitiv als Java-Objekte verwendet werden. Ektorp kann mit jeder beliebigen CouchDB verwendet werden, es verwendet das REST-API der Datenbank. Praktischerweise beinhaltet der TouchDB-Android-Port eine Ektorp-HTTP-Client-Implementierung, mit der man Ektorp auch für den Zugriff auf die lokale TouchDB konfigurieren kann. Der TouchDBHttpClient nutzt für die Kommunikation das bereits erwähnte touchdbURL-Schema der TouchDB. Die Wegpunkte eines Laufs sollen in unserer Lauf-App als Entität Trackpoint abgebildet werden (Listing 2). Ein Trackpoint beinhaltet dabei natürlich die Informationen über Längen- und Breitengrad der aktuellen Position. Darüber hinaus werden der aktuelle Name des Users, die ID des Laufs und der aktuellen Zeitpunkt gespeichert. Das Dokument ist damit inhaltlich kompatibel mit dem auf dem iPhone verwendeten Dokument. Auch auf dem Android-Smartphone werden diese Daten benötigt, um per MapReduce verschiedene Auswertungen, wie beispielsweise die Ermittlung aller Wegpunkte eines Laufs Listing 2: Die Entität Trackpoint public class Trackpoint extends CouchDbDocument implements Comparable<Trackpoint> { private String user; private String run; private double lon; private double lat; private Date time; ... } Mobile Technology 1 | 2013 Das Repository TrackpointRepository erbt von der Klasse CouchDbRepositorySupport<T>. Die Kontextklasse T ist in diesem Fall die Klasse Trackpoint. Das Repository benötigt im Konstruktor einen CouchDBConnector als Parameter, dessen Erzeugung in Listing 1 dargestellt ist. Das Speichern eines Trackpoints im LocationListener wird durch die auf dem Repository bereits existierende Methode add(trackpoint)trivial abgewickelt, wie in Listing 3 zu sehen ist. Bei Positionsänderungen wird somit jeweils ein Trackpoint in der lokalen TouchDB abgelegt, solange der Listener angemeldet ist. Das Tracking kann einfach durch das Abmelden des LocationListeners vom LocationManager beendet werden. Mit der Methode removeUpdates(listener) wird der zuvor angemeldete LocationListener wieder entfernt. Daten für Karten Wie auch auf dem iPhone soll es möglich sein, sich die gelaufene Strecke auf einer Karte anzusehen. Alle Wegpunkte des jeweiligen Laufs müssen dafür aus der lokalen Datenbank geladen werden. Die Karte sollte die Wegpunkte dann in der richtigen zeitlichen Reihenfolge als Pfad (wenn Anfangs- und Endpunkt nicht übereinstimmen) bzw. als Polygon (wenn Anfangs- und Endpunkt übereinstimmen) darstellen. Abfragen auf der CouchDB werden mithilfe von MapReduce [5] implementiert. Wie schon im ersten Teil des Artikels beschrieben, arbeitet MapReduce mit einem zweistufigen Verfahren. Zunächst werden die Dokumente mithilfe der Map-Funktion gruppiert. Die Funktion behandelt dabei genau ein einziges Dokument. Ist das Dokument für die Abfrage relevant, dann erzeugt die Map-Funktion ein Daten-Tupel für das Dokument. Das Ergebnis der Map-Funktion ist also eine Menge von Tupeln. Diese Tupel werden von der Reduce-Funktion ausgewertet. Beide Funktionen, sowohl Map als auch Reduce, müssen seiteneffektfrei sein, d. h. die Funktionen müssen für ein Dokument immer das www.mobile360.de
  • 5. CouchDB | ANDROID gleiche Ergebnis liefern, unabhängig von anderen Einflussfaktoren. Designdokumente und Views anlegen Die Views bzw. die Zugriffe auf Views werden im Repository der jeweiligen Entität definiert. Ektorp erlaubt eine annotationsbasierte Definition von Views auf Basis von JavaScript [6] über das REST-API der CouchDB. Listing 3: Das Trackpoint Repository ... private TrackpointRepository trackpointRepository = new TrackpointRepository(couchDBConnector); ... @Override public void onLocationChanged(Location location) { Trackpoint trackpoint = new Trackpoint(); trackpoint.setRun(runTitle); trackpoint.setUser(user); trackpoint.setLat(location.getLatitude()); trackpoint.setLon(location.getLongitude()); trackpoint.setTime(new Date()); trackpointRepository.add(trackpoint); } ... 039 Die TouchDB für Android unterstützt diese Funktion grundsätzlich, allerdings ist dafür das Projekt TouchDB-Android-JavaScript erforderlich. Unsere Lauf-App definiert die Views alternativ in Java und verwendet Ektorp lediglich, um auf die Views zuzugreifen (Listing 4). Die für die Erzeugung der View benötigte TDDatabase kann einfach mithilfe des in Listing 1 dargestellten TDServers erzeugt werden. Listing 4: View TrackpointsByRun private void createTrackpointsByRunView(TDDatabase database) { String viewName = "Trackpoint/trackpoint_by_run" TDView view = database.getViewNamed(viewName); view.setMapReduceBlocks(new TDViewMapBlock() { @Override public void map(Map<String, Object> document, TDViewMapEmitBlock emitter) { if (document.containsKey("run")) { String runId = document.get("run"); emitter.emit(runId, document.get("_id")); } } }, null, "1.0"); } Anzeige Anzeige www.mobile360.de 1 | 2013 Mobile Technology
  • 6. 040 ANDROID | CouchDB Die MapReduce-Funktionen einer CouchDB werden üblicherweise selbst in der Datenbank in so genannten „Designdokumenten“ gespeichert. Bei der Verwendung TouchDB hingegen müssen die Designdokumente bei jedem Start neu angelegt werden. Die Methode aus Listing 4 legt die View (bzw. die MapReduce-Funktion) trackpoint_by_run im Designdokument _design/Trackpoint an. Passenderweise liegt die Methode im TrackpointRepository aus Listing 3 und wird bei jedem Start der Lauf-App durchlaufen. designdokumente) des TrackpointRepositories anlegen, diese sind allerdings in Form von JavaScript definiert. Daher wäre hier wiederum das Projekt TouchDB-Android-JavaScript erforderlich. Die Wegpunkte eines Laufs werden so als Java-Objekt in chronologischer Reihenfolge zurückgeliefert. Eine Android-MapView kann sie jetzt sequenziell als Eckpunkte eines Polygons auf einer Karte darstellen. Übersicht der Läufe Der Benutzer soll die Möglichkeit haben, sich die Liste der absolvierten Läufe anzuschauen. Die Daten dafür Die Map-Funktion wird als anonyme Klasse implemen- befinden sich bereits im System, es stellt sich nun die tiert und implementiert das Interface TDViewMap- Frage, wie wir eine entsprechende Verdichtung auf die Block. Die Signatur der Funktion liefert zum einen das Läufe erreichen. Die bisherige Abfrage zielt auf die WegDokument als Map und einen TDpunkte eines einzelnen Laufs ab; ViewMapEmitBlock. Der Block auf den ersten Blick sieht es also baut nun ein Tupel in Form [Name so aus, als ob eine zweite View des Laufs, Dokument-ID] auf und benötigt wird. übergibt es mit dem Aufruf der Allerdings können wir für die emit()-Methode an den TDViewDaten der Übersicht die View MapEmitBlock. Das Resultat ist trackpoint_by_run wiedervereine View, die alle IDs von Dokuwenden. Die Ektorp-ViewQuery menten eines Laufs listet. Tabelle 1 bietet eine Gruppierung für alle zeigt einen Ausschnitt der durch die Ergebnisse eines Schlüssels an, Map-Funktion erzeugten Struktur. im Prinzip eine sehr einfache ReDer View-Inhalt wird, wie einduce-Funktion. Das Ergebnis der gangs schon erwähnt, in der SQLiteAbfrage liefert dann nicht mehr Datenbank gespeichert. So muss die ein Ergebnis pro Dokument Map-Funktion für ein Dokument (Tabelle 1), sondern die Menge nur ein einziges Mal durchgeführt an Ergebnissen pro Schlüssel. werden. Bei späteren Zugriffen wird Die zu dem Schlüssel passenden der gespeichert Werte verwendet Dokument-IDs werden, wie in und reduziert so die Laufzeit. Sollte Tabelle 2 als Auszug zu sehen ist, sich das Dokument ändern, dann als Menge zurückgegeben. wird die Map-Funktion erneut für Das TrackpointRepository das Dokument aufgerufen und der erhält so eine weitere Methode gespeicherte Wert aktualisiert. findRuns(), die die Menge von Läufen zurückgibt. Die MethoZugriff auf Views de ist in Listing 6 abgebildet. Anstelle des Suchkriteriums Der Zugriff auf den Inhalt von Abb. 4: Kartenansicht eines Laufs .key(String) (Listing 5) wird der Views ist mit Ektorp sehr einfach zu realisieren. Das TrackpointRepository wird um eine ViewQuery die Anweisung .group(true) übergeben. AlMethode findByRun(String) erweitert. Die Methode ternativ bestünde die Möglichkeit, eine weitere View anliefert die zeitlich sortierte Menge an Wegpunkten des zulegen, die die Dokumente entsprechend indiziert und verdichtet. An dieser Stelle sei nochmal auf das Buch „The Laufs als SortedSet, wie in Listing 5 zu sehen ist. Definitive Guide To CouchDB“ [7], [8] hingewiesen. Hier gibt es detaillierte Informationen zu MapReduce, @Override Re-Reduce und weiteren Themen rund um die CouchDB. public int compareTo(Trackpoint another) { Implementieren des Map-Blocks return time.compareTo(another.getTime()); } Das Auflösen eines Trackpoints anhand der DokumentID wird hier an die Methode get(String) des Repositories delegiert. Alternativ kann Ektorp das Ergebnis mit der Methode queryView(viewQuery, Class<T>) auch direkt in Trackpoints konvertieren. Ektorp müsste dafür vordefinierte Designdokumente (so genannte Standard- Mobile Technology 1 | 2013 Synchronisation Mit der Abfrage für die Übersicht an Läufen sind die Teile der App vollständig, die auf dem Andoid-Smartphone laufen. Die Einstiegsseite (Abb. 2) bildet die eben genannte Übersicht. Mit der Auswahl eines Laufs werden die einzelnen Wegpunkte des Laufs in Form von TrackpointObjekten ermittelt. Anhand der Trackpoints können dann Informationen wie zurückgelegte Distanz, Dauer www.mobile360.de
  • 7. CouchDB | ANDROID des Laufs oder durchschnittliche Geschwindigkeit dargestellt werden (Abb. 3). Zusätzlich kann sich der User seinen Lauf auf der Karte anschauen (Abb. 4). Ein ganz wesentlicher Teil der App fehlt hingegen noch: Die Daten der lokalen CouchDB sollen mit einer Instanz in der Cloud synchronisiert werden. Das Ganze hört sich aber weitaus komplizierter an, als es in Wahrheit ist. 041 Konfigurieren der Replikation Das Konfigurieren der Replikation ist wie schon erwähnt sehr einfach. Ektorp bietet hierfür die Klasse ReplicationCommand. Der Command wird mit einer Key Value "stefan-run-1" "b0bc34e2-dc0d-4e21-8b1f-d57edcc67607" Asynchronität "stefan-run-1" "b1d0be12-32b2-466d-a2bb-a118e458a764" Bevor wir uns dem Code widmen, soll hier auf den asynchronen Charakter der Synchronisation hingewiesen werden. Alle Wegpunkte aus der lokalen Datenbank werden in die Cloud repliziert. Hingegen werden lediglich die Wegpunkte des Benutzers aus der Cloud auf das Android-Smartphone transportiert. Zum einen macht dies aus Gründen des Datenschutzes Sinn, zum anderen wäre die gesamte Datenmenge allein aus Platzgründen eine Herausforderung für die lokale Datenbank des Smartphones. Die entsprechende Synchronisationsrichtung wird daher mit einem Filter konfiguriert. "stefan-run-1" "b3ea49c7-2dd2-4e15-8b46-6bc6ee00915a" "stefan-run-1" "b4f152ea-bd00-41ca-ab4f-6fa69629ec84" "stefan-run-1" "b608a7ef-894b-411b-a645-5f8fc0a66f61" Eine CouchDB in der Cloud Wie schon im ersten Teil des Artikels beschrieben, wird für die Synchronisation eine CouchDB-Instanz benötigt. Sollten Sie sich für die iPhone-App aus dem ersten Teil des Artikels bereits lokal eine CouchDB installiert [9] oder ein Iris-Couch-Konto angelegt haben [10], so können Sie diese Instanz weiterverwenden. Ansonsten müssen Sie sich an dieser Stelle für eine der beiden Alternativen entscheiden, da eine CouchDB wie gesagt notwendig ist. Tabelle 1: Ergebnis der Map-Funktion Listing 5: Zugriff auf die Wegpunkte eines Laufs public SortedSet<Trackpoint> findByRun(String runId) { ViewQuery viewQuery = new ViewQuery() .designDocId("_design/Trackpoint") .viewName("trackpoint_by_run") .key(runId); ViewResult result = db.queryView(viewQuery); Set<Trackpoint> trackpoints = new TreeSet<Trackpoint>(); for (Row row : result.getRows()) { Trackpoint trackpoint = get(row.getValue()); trackpoints.add(trackpoint); } return trackpoints; } Einrichten des Filters Dieser Absatz ist für Sie nur interessant, wenn Sie eine neue CouchDB angelegt haben. Sollten Sie die CouchDB bereits für das iPhone-Beispiel konfiguriert haben, dann können Sie den Abschnitt überspringen. Zunächst benötigen wir eine Datenbank auf der CouchDB, diese kann entweder über curl -X PUT http://127.0.0.1:5984/couch25k oder über Futon, das integrierte Browser-Admin-UI einer jeden CouchDB, angelegt werden. Futon erreichen Sie immer unter dem URL http://<host>:5984/_utils/. Bei der Konfiguration der Replikation kann für jede Richtung eine Filterfunktion referenziert werden, die für die Synchronisation angewendet wird. Die Filterfunktion muss dabei jeweils auf der Seite der Quelle definiert sein, so werden nur die auf dem Filter passenden Dokumente übertragen. Eine Definition auf der Zielseite wäre sehr ineffizient, da zunächst alle Dokumente übertragen werden müssten und erst auf der Zielseite über die Relevanz des Dokuments geurteilt werden könnte. Die Filterfunktionen werden in JavaScript formuliert und für unsere Lauf-App im Designdokument _design/couch25k in der CouchDB gespeichert. Listing 7 zeigt die für die Synchronisation verwendete Filterfunktion by_user. Die Funktion vergleicht den Wert des Feldes user eines Dokuments mit dem Request-Parameter username der Abfrage. Ist der Filter aktiv, dann werden nur Dokumente repliziert, auf die diese Bedingung zutrifft. www.mobile360.de Listing 6: Zugriff auf die Liste von Läufen public SortedSet<String> findRuns() { ViewQuery viewQuery = new ViewQuery() .designDocId("_design/Trackpoint") .viewName("trackpoint_by_run") .group(true); ViewResult result = db.queryView(viewQuery); SortedSet<String> runIds = new TreeSet<String>(); for (Row row : result.getRows()) { runIds.add(row.getKey()); } return runIds; } Listing 7: Die Filterfunktion für die Synchronisation { "_id": "_design/couch25k", "_rev": "10-fe6de55bfd887a6c777a0a712cc0b6b3", "language": "javascript", "filters": { "by_user": "function(doc, req) { return doc.user == req.query.username; }" } } 1 | 2013 Mobile Technology
  • 8. 042 ANDROID | CouchDB Quelle und einem Ziel konfiguriert. Es können entweder URLs für entfernte Datenbanken oder Namen von lokal zur Verfügung stehenden Datenbanken verwendet werden. Ein ReplicationCommand behandelt somit immer eine einzelne Replikationsrichtung. Der Upstream, also das Übertragen der lokalen Daten in die Cloud, wird wie in Listing 8 konfiguriert. Als Quelle wird der Name der lokalen Datenbank couch25k angegeben, das Ziel ist der URL der CouchDB in der Cloud. Die entgegengesetzte Richtung ist unwesentlich komplizierter, da hier der Filter konfiguriert wird (Listing 9). Die Replikation verweist auf den in der CouchDB definierten Filter by_user im Designdokument couch25k. Die Filterkriterien werden als Map übergeben und in der Filterfunktion mit ausgewertet. Nur der Vollständigkeit Listing 8: Die Synchronisation in die Cloud ReplicationCommand push = new ReplicationCommand.Builder() .source("couch25k") .target("http://stefanreichert.iriscouch.com:5984/couch25k") .continuous(true) .build(); try { couchDBInstance.replicate(push); } catch (Exception exception) { Log.e(TAG, exception.getMessage(), exception); } Listing 9: Die Synchronisation aus die Cloud mit Filter Map<String, Object> queryParams = new HashMap<String, Object>(); queryParams.put("username", "stefan"); ReplicationCommand pull = new ReplicationCommand.Builder() .target("couch25k") .source("http://stefanreichert.iriscouch.com:5984/couch25k") .filter("couch25k/by_user") .queryParams(queryParams) .continuous(true) .build(); try { couchDBInstance.replicate(pull); } catch (Exception exception) { Log.e(TAG, exception.getMessage(), exception); } halber sei gesagt, dass auch Filter für die andere Replikationsrichtung unterstützt werden [11]. Die Synchronisation läuft im Hintergrund und gleicht die Dokumente beider Datenbanken zeitnah ab. Bei bestehender Verbindung zum Internet werden so die lokalen Daten automatisch in die zentrale Datenbank repliziert. Fazit Im zweiten Teil des Artikels haben Sie gesehen, wie CouchDB auch auf Android-Geräten eingesetzt werden kann. TouchDB für Android erlaubt es, auch für diese Plattform auf einfache Art und Weise offlinefähige Apps zu entwickeln, die, wenn möglich, im Hintergrund lokale Daten und Daten in der Cloud synchronisieren. Dabei besteht auch hier die Möglichkeit, die Synchronisation mittels Filter ökonomisch und sinnvoll zu konfigurieren, und damit den besonderen Gegebenheiten auf mobilen Endgeräten Rechnung zu tragen. Hervorzuheben ist, dass es die unterschiedlichen TouchDB-Ports erlauben, Apps für unterschiedliche Plattformen mit gleicher Architektur zu entwickeln. Die erfassten Dokumente sind dabei weitgehend kompatibel zueinander. Bei der Entwicklung der App können so alle Vorteile der jeweiligen Plattform voll ausgenutzt werden. Stefan Reichert, Diplom-Wirtschaftsinformatiker (FH), arbeitet als Software Engineer bei Zühlke Engineering in Hamburg. Er beschäftigt sich seit mehreren Jahren mit verteilten Java-Enterprise-Anwendungen, serviceorientierten Architekturen, Eclipse und Eclipse RCP. Seit einiger Zeit begeistert ihn die Entwicklung von Apps mit Android. Darüber hinaus ist er Buchautor und verfasst regelmäßig Fachartikel. stefan@wickedshell.net, http://www.wickedshell.net/blog Peter Friese arbeitet als Software Architect und Principal Consultant für Zühlke Engineering. Seine Schwerpunkte liegen auf der Entwicklung von plattformübergreifenden mobilen Lösungen (u. a. für iOS, Android, Windows Phone) sowie der modellgetriebenen Entwicklung (MDSD). Peter bloggt auf http://www.peterfriese.de. @peterfriese. Links & Literatur [1] http://guide.couchdb.org/editions/1/en/replication.html [2] https://github.com/couchbaselabs/TouchDB-Android [3] http://www.ektorp.org [4] http://jackson.codehaus.org [5] Jeffrey Dean and Sanjay Ghemawat: „MapReduce: Simplified Data Processing on Large Clusters“: http://research.google.com/archive/ mapreduce.html [6] http://ektorp.org/tutorial.html#d0e105 Key Value "stefan-run-1" {"b0bc34e2-dc0d-4e21-8b1f-d57edcc67607", "b1d0be12-32b2-466d-a2bb-a118e458a764", "b3ea49c7-2dd2-4e15-8b46-6bc6ee00915a", "b4f152ea-bd00-41ca-ab4f-6fa69629ec84", "b608a7ef-894b-411b-a645-5f8fc0a66f61"} Tabelle 2: Ergebnis der Map-Funktion mit Gruppierung Mobile Technology 1 | 2013 [7] J.Chris Anderson, Jan Lehnardt, Noah Slater: „CouchDB: The Definitive Guide“, O’Reilly Media 2010 [8] http://guide.couchdb.org/ [9] http://wiki.apache.org/couchdb/Installation [10] http://www.iriscouch.com [11] https://github.com/couchbaselabs/TouchDB-Android/wiki/TouchDBEktorp-Replication www.mobile360.de