Ein Tool, welches die Produktivität bei der Entwicklung hybrider Web-Apps erheblich verbessert, ist der Chrome-Ripple-Emulator.
Das Testen mit dem Android-SDK-Emulator kostet viel Zeit für das Build sowie das Launchen der Anwendung im Emulator. Die Android-Emulatoren bieten allerdings den Vorteil, das sie das Testen für viele verschiedene Betriebssystem-Versionen und Endgeräte-Typen ermöglichen, weshalb das SDK im Entwicklungsprozess nicht überflüssig wird.
Der Ripple-Emulator dagegen eignet sich für die effiziente Programmierung der App als solche, solange neue Funktionalitäten während eines Entwicklungszyklus aufgebaut und vorläufig getestet werden.
Vor allem auch die Sensorik von mobilen Endgeräten kann mit dem Ripple-Emulator viel besser getestet werden, als mit dem SDK-Emulator.
Das CordovaMapSample
Im Folgenden wird der Einsatz des Ripple-Emulators vorgestellt werden und zwar nicht wie im letzten Beitrag am Beispiel des simplen Hello-World-Projekt, das bei New Project einer Cordova-Application als Start für ein eigenes Projekt generiert wird, sondern mit dem ziemlich eindrucksvollen CordovaMapSample von Netbeans.
Man findet CordovaMapSample, wenn man in NetBeans unter New Project die Html5-Samples ansteuert (Abb. 1):
Abb. 1: Netbeans: New Project / Samples / Html5 / Cordova Maps Sample
Diese Anwendung kann man jetzt aus der Maske (Abb. 1) heraus öffnen und beispielsweise unter dem vorgeschlagenen Namen unter htdocs speichern. NetBeans kopiert dann alle erforderlichen Ressourcen des Beispiels in den gewählten Ziel-Ordner und legt gleichzeitig dafür ein neues Projekt an.
Dieses Projekt enthält z.B. einen Kompass, einige interessante und schon recht ausgefeilte Anwendungen für Google-Maps, usw. Der ideale Ausgangspunkt also, um unseren Workflow aus dem vorherigen Beitrag dahingehend auszubauen, dass wir Anwendungen entwickeln können, die den vollen Umfang der verfügbaren Handy-Sensorik sowie weitere übliche Features eines mobilen Endgerätes nutzen.
Wir machen uns bewußt, dass dies mit einer reinen responsiven Webseite ohne die hybride Funktionalität von Cordova nicht geht.
Testen von CordovaMapSample mit einem Android SDK Emulator
Zunächst machen wir unsere Beispiel-App lauffähig in der Umgebung, die wir schon aus dem letzten Beitrag (vgl. dort Abb. 4) kennen. Wir hatten ja zwei Emulatoren im SDK definiert, einen für Android 4.2.2 und einen für Android 2.2 (Froyo).
Damit wir unser Beispiel damit testen können, müssen wir wieder je eine Zeile in folgenden beiden Dateien ändern:
- C:\xampp\htdocs\PhonegapTest\platforms\android\AndroidManifest.xml
- C:\xampp\htdocs\PhonegapTest\platforms\android\project.properties
Diese Dateien bzw. der ganze Ordner Android unter platforms existiert aber noch gar nicht, weil wir noch kein Build erstellt haben.
Wir compilieren unser Projekt deshalb, indem wir
- einen Android-Emulator mit dem Android Virtual Device Manger starten, z.B. den für Android 4.2.2 (vgl. voriger Beitrag Abb. 4)
- als Zielplattform in NetBeans Cordova (Android Emulator) einstellen (grünes Handy-Symbol / Android Emulator),
- mit der rechten Maustaste auf dem Projekt-Ordner CordovaMapsSample den Menupunkt run aufrufen (und vorher am besten noch ein clean & build).
NetBeans findet dann den offenen Emulator automatisch und beginnt mit der Erstellung des Builds.
Dieses Build bricht erwartungsgemäß mit einem BUILD FAILED ab, denn jetzt erst können wir, nachdem NetBeans unter platforms den Ordner android angelegt hat, dort die Dateien AndroidManifest.xml und project.properties bearbeiten.
Wie im vorigen Beitrag passen wir die uses-sdk-Zeile in AndroidManifest.xml an unsere Zielplattformen an, tragen also maximalen, minimalen bzw. Target-Level ein:
<uses-sdk android:maxSdkVersion="17" android:minSdkVersion="8" android:targetSdkVersion="17" />
In der Datei project.properties im gleichen Ordner stellen wir folgendes ein:
target=android-17
Jetzt starten wir run erneut. Eigentlich haben wir alles richtig gemacht bis hierher. Es müsste laufen, tut es aber nicht.
Die Analyse der Fehlermeldung ergibt, dass das Build scheitert, wenn es versucht, das cordova-plugin-device-orientation.git herunterzuladen. Dabei wird versucht, Ressourcen in diesem Ordner abzulegen.
- C:\Users\Admin\AppData\Local\Temp\plugman\git
Der Ordner plugman\git existierte bei mir nicht. Ich habe ihn manuell erzeugt und danach lief das Build fehlerfrei durch. Auf dieses Problem habe ich schon im letzten Beitrag hingewiesen.
Allerdings verfing sich der Prozess dann wieder beim Hochladen der apk-Datei zum Emulator.
Ein Abbruch des Build und Neustart mit run führten dann beim nächsten Versuch zum Erfolg. Die App wurde automatisch im Emulator gestartet (vgl. Abb. 2).
Falls weitere Probleme auftreten, bekommt man genauere Fehlermeldungen, wenn man das Build manuell in einer Windows-Console in folgendem Ordner startet:
C:\xampp\htdocs\CordovaMapsSample\platforms\android\cordova\
run --emulator ... bzw. bei angeschlossenem mobilen Endgerät run --device
Die Fehlermeldungen sind dann wesentlich aussagekräftiger als die, welche im Output-Fenster von NetBeans angezeigt werden. In der Regel kommt man dem Problem damit schnell auf die Schliche.
Abb. 2: Das Cordova Maps Sample im Emulator
Die App enthält eine Vielzahl von typischen teilweise sehr anspruchsvollen Web-Anwendungen wie z.B. einen Kompass, Adress-Suche und sogar Routing mit Google-Maps sowie einige weitere interessante Features.
Wie allerdings soll man mit dem Emulator jetzt z.B. den Kompass testen (vgl. Abb. 3)? Das geht grundsätzlich, ist aber umständlich. Deswegen wird dieser Weg hier nicht weiter verfolgt, denn es gibt ja den Ripple-Emulator.
Abb. 3: Das Cordova Maps Sample im Emulator (Menupunkt "Kompass")
Mir war es leider nicht möglich, die Anwendung in einem Android 2.2 (Froyo) - Emulator ans Laufen zu bringen. Zwar wurde die App ordentlich gelaunched, aber der Bildschirm blieb schwarz. Mit meinem Android 2.2 - Handy funktionierte es dagegen. Auf einem Android 4.2.2 - Emulator gab es ebenfalls keine Probleme (s. Abb. 2).
Auf die Sensorik des Handys hat man zwar jetzt mit Cordova Zugriff, aber es fehlen komfortable Möglichkeiten, diese Sensorik zu testen. Hierfür eignet sich bis zu einem gewissen Grad hervorragend der Chrome-Ripple-Emulator, der außerdem in Bezug auf die Produktivität des Entwicklungsprozesses noch einige weitere Vorteile bietet. Wie man dieses geniale Tool installiert, wird in folgendem gezeigt.
Installation der Ripple-Emulators
Es gibt für Chrome zwar eine Ripple-Emulator-Erweiterung, aber die ist totally depricated. Solltet ihr auf sowas stoßen, Finger weg! Und wenn eine solche Extension im Chrome-Browser schon vorhanden ist, bitte deinstallieren.
Ich installiere mir Ripple nur mit npm:
npm install -g ripple-emulator
Dann gehe ich in den platforms/android/assets/www-Ordner meines Corodova-Projekts, in unserem Fall das CordovaMapsSample-Projekt:
- C:\xampp\htdocs\CordovaMapsSample\platforms\android\assets\www
Ich rufe dort eine Windows-Console auf und gebe ein:
ripple emulate
Der Chrome-Browser wird nun mit der Sample-App gestartet (vgl. Abb. 4).
Abb. 4: Ripple-Emulator im Chrome-Browser mit der Sample Map App
Betätigen wir dort jetzt den Menupunkt Compass und öffnen auf der rechten Seite das Geo Location Menu (vgl. Abb. 5), wird sofort ein wichtiger Vorteil des Ripple-Emulators deutlich.
Abb. 4: Ripple-Emulator im Chrome-Browser mit der Sample Map App und Compass-Auswahl
Verschiebe ich dort nämlich den Slider für Heading etwas, dreht sich der Kompass. Man sieht also, dass sich mit dem Rippel-Emulator sehr schön die Sensorik eines mobilen Geräts testen lässt. Dies ist mit den Android-SDK-Emulatoren sehr viel schwieriger.
Ein weiterer Vorteil ergibt sich daraus, dass ich mit dem Ripple-Emulator sehr viel produktiver entwickeln kann. Eine Änderung im Source-Code ist wesentlich schneller sichtbarer, denn schon das Uploaden und Launchen eines apk-Files für einen SDK-Emulator oder ein reales Handy benötigt viel Zeit.
Dies lässt sich noch einmal beschleunigen durch den Einsatz eines kleinen Grunt-Skripts für das Life-Kopieren von geänderten Sourcen in das Build hinein.
Life-Kopieren von geänderten Sourcen in das Build mit Hilfe von Grunt
Folgendes Grunt-Skript, dass inklusive der zugehörigen package.json-Datei und dem Ordner node_modules einfach in den Projekt-Ordner kopiert werden kann, wenn es einmal angelegt ist, beschleunigt die Entwicklung noch einmal ungemein, da es die sehr lange Compilierung des Build von NetBeans bzw. Cordova weitgehend überflüssig macht.
Nicht ganz, denn beim ersten Mal bzw. nach gravierenden Änderung des Projekts muss man ein Build ordentlich erzeugen. Aber für inkrementelle Änderungen und deren Tests ist dieses Grunt-Skript hervorragend geeignet. Den Kern des Skripts habe ich hier gefunden.
module.exports = function(grunt){ grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), shell : { "build" : { command : [ 'build.bat', ].join('&&'), options: { stderr: true, execOptions: { cwd: 'platforms\\android\\cordova\\' } } }, "clean" : { command : [ 'clean.bat', ].join('&&'), options: { stderr: true, execOptions: { cwd: 'platforms\\android\\cordova\\' } } }, "runEmulator" : { command : [ 'run.bat --emulator', ].join('&&'), options: { stderr: true, execOptions: { cwd: 'platforms\\android\\cordova\\' } } }, "runDevice" : { command : [ 'run.bat --device', ].join('&&'), options: { stderr: true, execOptions: { cwd: 'platforms\\android\\cordova\\' } } }, }, exec:{ prepare:{ command:"cordova prepare", stdout:true, stderror:true } }, watch:{ files:['www/**/*.*'], tasks:['exec:prepare'] } }); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-exec'); grunt.loadNpmTasks('grunt-shell'); grunt.registerTask('default', ['watch']); grunt.registerTask('clean', ['shell:clean']); grunt.registerTask('build', ['shell:build']); grunt.registerTask('clean build', ['shell:clean', 'shell:build']); grunt.registerTask('runEmulator', ['shell:runEmulator']); grunt.registerTask('runDevice', ['shell:runDevice']); };
Das Skript benötigt kein grunt-sync wie in meinem Beitrag zu den Kopier-Methoden von Grunt, sondern verwendet stattdessen den Cordova-Befehl prepare.
Da ich - wie schon erwähnt - mit dem Durchlaufen des Build & Run bei mir nach Umstellung auf das aktuelle NetBeans 8.0.2 Probleme hatte, habe ich noch einige Tasks für clean, build, clean build, runEmulator und runDevice hinzugefügt, so dass man z.B. ein Run aus dem Projektordner mit Grunt starten kann.
Dies hat auch den Vorteil, dass man die entsprechenden Aktionen von NetBeans mit diesen Grunt-Tasks verbinden kann. Nach dem Kopieren eines Grunt-Skripts in den NetBeans-Ordner wird man nämlich danach gefragt, ob man die Aufgaben für clean, build und clean build an Grunt übertragen will, wenn man diese Aktionen ausführen möchte. Leider haben die NetBeans-Entwickler die Aufgabe run vergessen, dann wär's echt praktisch gewesen.
Wer den einen oder anderen meiner Beiträge zum Thema Grunt gelesen hat (z.B. den hier), der dürfte keine Probleme haben mit der Implementierung dieses Skripts.
Alles zusammen: Der Workflow auf Knopfdruck
Alle bisherigen Aktivitäten kann man in einem einzigen Bash-File zusammenfassen, welches es ermöglicht, alle erforderlichen Aktionen für den Aufruf von NetBeans, Apache, MySql, Chrome, Ripple und Grunt mit einem Klick auszuführen:
@echo off REM Diesen Batch nicht (!) als Admin aufrufen REM Parameter für start: http://wiki.winboard.org/index.php/Mehrere_Programme_per_Batch-Datei_starten start "Apache" /B "C:\xampp\apache_start.bat" start "Mysql" /B "C:\xampp\mysql_start.bat" start "Netbeans" /B "C:\Program Files\NetBeans 8.0.2\bin\netbeans64.exe" REM Bei folgenden Programmen erscheint die Meldung: "Ein nicht-identifiziertes Programm möchte auf Ihren Computer zugreifen" REM Diese Meldung kann nur unterdrückt werden durch das Ausschalten der Benutzerkontensteuerung, was nicht zu empfehlen ist REM start "Mercury" /B "C:\MERCURY\mercury.exe" REM start "XAMPP" /B "C:\xampp\xampp-control.exe" cd %~dp0 start "Grunt waits on changed sources" /B grunt REM Warte .... @ping -n 10 localhost> nul start "Chrome Unsecure" /B "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" yetispeed.phonegap.de --disable-web-security REM Warte .... @ping -n 5 localhost> nul REM Achtung, ripple muss auch für den Admin per Umgebungsvariable erreichbar sein cd %~dp0\platforms\android\assets\www ripple emulate exit
Der Start von Apache und MySql ist für dieses Beispiel noch nicht notwendig, aber er wird für zukünftige Projekte benötigt, so dass ich dann auf dieses Skript hier verweise. Ihr könnt euch das Skript ja leicht an eure Umgebung und Bedürfnisse anpassen. Es ist auch konzeptuell eine gute Zusammenfassung des hier beschriebenen Workflows.
Das Skript wird einfach zusammen mit den Grunt-Ressourcen (s. oben) in ein neues NetBeans-Cordova-Projekt kopiert. Dann nur noch eine Verknüpfung für den Desktop oder sonstwo hin erstellen und schon kann man den ganzen Entwickler-Stack auf Knopfdruck starten, so dass man sich auf das kreative Programmieren beschränken kann. Ein absolutes Muss für Mausschubser, die nicht permanent Kommandozeilen in Consolen eingeben wollen.
Der Chrome-Browser wird übrigens im Unsecure-Modus gestartet, weil es in bestimmten Fällen während der Entwicklung sinnvoll ist, dass die Ausführung von Javascript weniger restriktiv ist.
Fazit
Wir haben zunächst konventionell mit den Android-SDK-Emulatoren das CordovaMapsSample von NetBeans zum Laufen gebracht. Dieses eignet sich für die vorliegende Problemstellung, da sich am Beispiel des integrierten Kompass die Vorteile von Ripple bei der Nutzung der Sensorik von mobilen Endgeräten in hybriden Web-Apps zeigen lassen.
Nachdem dies gelungen ist, wurde der Ripple-Emulator installiert und die Vorteile am Beispiel des Kompass gezeigt. Außerdem wurde durch den Einsatz des Ripple-Emulators die Performance des Workflow bei der Entwicklung hybrider Apps erheblich gesteigert.
Dann wurde ein Grunt-Skript vorgestellt, das die Performance des Entwickler-Workflows noch einmal verbessert.
Als letztes wurden alle Schritte des Entwickler-Zyklus in einem Bash-File zusammengefaßt, so dass dem Entwickler der komplette Stack zur Entwicklung der hybriden App auf Knopfdruck (bzw. mit einem Klick) zur Verfügung steht und er sich auf die kreative Programmierung konzentrieren kann.