… die garantiert schnell wirken

Die Top-5-Performancetipps für Cross-Plattform-HTML5-Anwendungen, …
Kommentare

HTML5 ist langsam? Nein, ganz im Gegenteil! Heutzutage zeigt sich die grundlegende Geschwindigkeit von Cross-Plattform-HTML5-Anwendungen in einem sehr positiven Licht. Dennoch gibt es einige Fallstricke, in denen sich der Entwickler schnell verfängt, wenn er nicht auf ein paar Punkte achtet. Dieser Artikel geht zuerst auf die Vorgehensweise der Performancemessung ein. Anschließend werden die Top-5-Performancetipps gezeigt, mit denen sich noch einmal ordentlich Power herausholen lässt.

Bevor wir nun auf die Performanceaspekte eingehen, muss zunächst analysiert werden, an welchen Punkten genau Zeit beansprucht wird. Hybrid-Apps sind gegenüber den klassischen Web-Apps schon um einiges performanter. Ein Grund hierfür ist, dass der komplette Inhalt zu Beginn einmal vollständig geladen wird und anschließend auf dem lokalen Speicher des Browsercaches liegt. Erst wenn neue Informationen bezogen werden müssen, wird wieder mit dem Server kommuniziert. Welche Fallen können also unter HTML5-Hybrid-Apps entstehen?

Geschwindigkeit von Hybrid-Apps testen

Für das Testen der Geschwindigkeit bietet das kostenfreie Intel XDK eine angenehme Profiling-Lösung. Die Funktionalität ist aktuell auf Android-Geräte beschränkt und wird direkt per USB mit dem gewünschten Gerät genutzt. Zu Beginn muss man sicherstellen, dass die Intel-App-Preview-App auf dem Zielgerät installiert ist. Das Android-Device benötigt dann einen aktiven Developer-Modus. Dieser wird über die Android-Einstellungen beim Menüpunkt Über…, Softwareinformationen aktiviert, indem man exakt siebenmal auf die Build-Nummer tappt. In den Einstellungen ist anschließend ein neuer Menüpunkt mit Entwickleroptionen sichtbar. Aktivieren Sie hier USB-Debugging. Zusätzlich muss bei den Einstellungen unter Sicherheit die Unbekannte Herkunft erlaubt werden.

Das Profiling kann jetzt im Intel XDK unter dem Profile-Tab beginnen. Konnte eine Verbindung sauber aufgebaut werden, sieht der Bildschirm aus wie in Abbildung 1. Zur Auswahl steht das CPU- oder Memory-Profiling. Als Beispiel wird die Open-Source-Hybrid-App XCardboard geladen und einmal per CPU getestet. Dazu den Play-Button betätigen; sobald die App zum Hauptbildschirm mit der Panoramasicht wechselt, wird auf den roten Record-Button geklickt. Anschließend wird das mobile Gerät kurz von links nach rechts geschwenkt und der Klick auf den Record-Button wiederholt. Die Auswertung zeigt, dass die App sauber optimiert ist und sechs Prozent CPU vom Kompass-Sensor-Plug-in verbraucht wird (Abb. 2).

Abb. 1: Profiling mit dem Intel XDK

Abb. 1: Profiling mit dem Intel XDK

Abb. 2: Der CPU-Verbrauch von XCardboard

Abb. 2: Der CPU-Verbrauch von XCardboard

Einen weiteren, umfangreichen Profiler bieten die Chrome-Developer-Tools, die die Ladezeiten von Seiten, Grafiken und der Datenkommunikation ermitteln. Beim Intel XDK steht dieses Werkzeug innerhalb des Debug-Tabs zur Verfügung. Mit einem Klick auf das Käfersymbol, wird die App im Debug-Modus auf dem Device ausgeführt. Als Beispiel wird erneut XCardboard gestartet. Im Menü wird nun ein Onlinepanorama ausgewählt. Bei den Chrome-Developer-Tools sieht man unter Network, wie das Bild aus dem Netz geladen wird. Der Vorgang dauert etwas über 400 ms. Optimierungsbedarf ist demnach in jedem Fall vorhanden. Abbildung 3 veranschaulicht die ermittelten Daten innerhalb der Chrome-Developer-Tools im Zusammenspiel mit dem Intel XDK.

Abb. 3: Ladezeiten mit den Chrome-Developer-Tools ermitteln

Abb. 3: Ladezeiten mit den Chrome-Developer-Tools ermitteln

Tipp 1: CSS Sprite Sheets verwenden

Wie die soeben ausgeführte Performanceanalyse bestätigt, wird die meiste Zeit oft beim Laden von weiterem Inhalt aus dem Netz verbraucht. Neben Texten sind das häufig auch Bilder. Natürlich ist die schnellste Variante das direkte Ausliefern der App mit integrierten Bildern. Die Praxis zeigt aber, dass immer wieder neuer Inhalt aus dem Netz benötigt wird.

Eine neue Technik, die seit 2014 bei Webentwicklern zum Einsatz kommt, ist die Verwendung von CSS Sprites. Dabei handelt es sich um eine einzelne Grafikdatei, die mehrere Grafiken beinhaltet. Die so genannten Sprites sind bei Spieleentwicklern schon seit Jahrzehnten gang und gäbe. Hier galt vor allem der Vorteil beim schnellen Laden der Bilder vom Speicher aus. In der Webwelt geht es um die Verringerung der Webseitenladezeit. Denn der Webbrowser lädt wesentlich schneller eine 10 kB große Datei, als zehn Dateien mit jeweils 1 kB, da die HTTP-Spezifikation nur wenige parallele Downloadvorgänge vorsieht.

Einen weiteren Vorteil gibt es beim Austauschen von Grafiken zur Laufzeit, wie es zum Beispiel beim Hover-Effekt der Fall ist. Denn der Wechsel der Grafik erfordert ohne weitere Vorkehrungen immer eine gewisse Ladezeit, womit der Benutzer eine kurze Verzögerung oder ein kurzes Flackern wahrnimmt. Bei der Sprite-Technik dagegen ist das zweite Bild bereits Bestandteil der Gesamtgrafik und der sichtbare Bereich muss nur noch verschoben werden. Ein Flackern nimmt der Benutzer nicht mehr wahr und ein erneuter HTTP-Aufruf bleibt auch erspart.

In Abbildung 4 sehen wir eine typische Sprite-Bilddatei, wie sie für Icons oft zum Einsatz kommt. In Listing 1 wird der CSS-Code gezeigt, der nur das benötigte Herz-Icon aus icons.png anzeigen lässt. Das Herz befindet sich 90 Pixel horizontal (X) nach rechts, beginnend vertikal (Y) bei 0 Pixel. Als Fläche reichen hierbei 20 Pixel aus.

Abb. 4: Eine Sprite-Bilddatei: icons.png

Abb. 4: Eine Sprite-Bilddatei: icons.png

Listing 1: CSS-Sprite-Code zum Anzeigen des Herz-Icons aus icons.png
<!DOCTYPE html>
<html>

  <head>
    <title>CSS Sprite Sample</title>
    <style>
      .icon {
        width: 20px;
        height: 20px;
        background-image: url("images/icons.png");
      }
      
      .icon-heart {
        background-position: -90px 0;
      }
    </style>
  </head>

  <body>
    <div class="icon icon-heart"></div>
  </body>
</html>

Der Nachteil liegt beim zusätzlichen Aufwand für das Erstellen von Sprite-Bilddateien und dem Schreiben von CSS-Code ohne zusätzliche Tools wie SpritePad. Ein weiteres Problem ist das Laden einer großen Sprite-Datei, obwohl nicht alle Grafiken für die aktuelle Seite benötigt werden. Es ist daher wichtig, sich vorher Gedanken zu machen, was wirklich notwendig ist.

Tipp 2: Einheitliche HTML5-Application-Runtime nutzen

Die großen Schmerzen bei der Webentwicklung bleiben unverändert. Was bei einem Browser gut aussieht und funktioniert, läuft bei einem anderen ganz anders als erwartet. Dabei spielt es keine Rolle, ob es der Internet Explorer, Firefox, Opera, Safari oder Chrome ist. Am besten läuft die Webanwendung immer beim Standardbrowser, auf dem entwickelt wird. Das liegt unter anderem daran, dass sich die gewohnten Eigenheiten routiniert handhaben lassen. Zugegeben, es hat sich hierbei auch einiges verbessert, jedoch existiert die Angelegenheit noch immer. Das Problem ist in der mobilen Welt nicht anders; es ist sogar noch schlimmer. Nicht nur, dass eine Hybrid-App auf Android anders als auf iOS funktionieren kann, auch auf der eigenen Plattform gibt es Schwierigkeiten. Mit jeder neuen Android-Version wurde die Chrome- Browser-Engine um weitere HTML5-Features erweitert und eine bessere Performance herausgeholt. Das kann sich allerdings von Gerät zu Gerät unterscheiden. Denn am Ende entscheidet der Anbieter, was genau bei seinem Android-Derivat bereitgestellt wird. Das bedeutet, man kann sich nicht darauf verlassen, dass wenn etwas auf dem eigenen Android-4.3-Gerät gut läuft, es sich bei einem anderen Android 4.3 genauso verhält. Beispielsweise passiert es oft, dass man auf HTML5-Features zugreift, die auf einem anderen Android-Gerät nicht funktionieren. Die Geschwindigkeit fällt genauso anders aus, obwohl die Geräteleistung stimmt. Das ist nachteilig für den Hybrid-App-Entwickler.

Zur Lösung der Probleme gibt es für Android und Tizen das Open-Source Crosswalk Project. Hierbei handelt es sich um einen Chromium-Browser auf dem neuesten HTML5-Stand, der eine hohe Performance bietet. Er hostet die Hybrid-App auf dem Zielgerät, unabhängig vom Standardwebbrowser. Ein Nachteil dabei ist, dass die Paketgröße der App durchschnittlich 15 bis 20 MB beträgt. Das liegt daran, dass ein kompletter Browser ohne Abhängigkeiten in der eigenen Hybrid-App bereitgestellt wird. Für Hybrid-Apps in der Geschäftswelt sollte die Dateigröße aber kein Hindernis darstellen. Unterstützt wird Crosswalk ab Android 4 und höher, bei Tizen ab der Version 3.

Das Intel XDK bietet einen kostenlosen Crosswalk-Build-Server in der Cloud. Dazu innerhalb vom XDK auf dem Build-Tab wechseln und bei „Crosswalk for Android“ auf den Build-Button klicken (Abb. 5). Das aktuelle Projekt wird auf den Cloud-Server hochgeladen, mit einem weiteren Klick auf BUILD wird die Hybrid-App mit integriertem Chromium-Crosswalk-Browser kompiliert. Auf der Projektseite gibt es eine ausführliche Anleitung, um Crosswalk unabhängig vom XDK nutzen zu können.

Abb. 5: Crosswalk-Hybrid-App erstellen

Abb. 5: Crosswalk-Hybrid-App erstellen

Bei iOS und Windows gibt es das Herstellerproblem nicht. Wer dennoch mehr Power unter iOS wünscht, kann das neue Cordova-Plug-in WKWebView nutzen. Das Plug-in ist auf iOS 8 beschränkt und kann wie gewohnt als Cordova-Plug-in registriert werden. Das WKWebView-Plug-in wechselt anschließend automatisch zur Laufzeit das Standard-UIWebView-Control aus. Die dazugewonnene Performance ist beachtenswert, wie Abbildung 6 zeigt.

Abb. 6: Performancevergleich von UIWebView und WKWebView

Abb. 6: Performancevergleich von UIWebView und WKWebView

Ein Cordova-Plug-in wird beim Intel XDK über den Projects-Tab hinzugefügt. Ist der Projects-Tab sichtbar, wird bei Plugins | Third-Party Plugins auf den Get Plugin From The Web-Button geklickt. Beim folgenden Dialog schreibt man als Name wkwebview, die Plug-in-ID lautet com.telerik.plugins.wkwebview. Zu guter Letzt muss ein Haken bei Plugin Is Located In The Apache Cordova Plugins Registry gesetzt werden.

Tipp 3: Auf optimierte Query-Selectors setzen

Die Stärke von JQuery hat wohl einige Entwickler für JavaScript begeistern können. Gerade auch, wenn diese nicht aus der Webwelt stammen. Mit JQuery kann man auf DOM-Elemente zugreifen und diese leserlich manipulieren. Die integrierte Query-Selector-Funktionalität ermöglicht zudem einen komplexen Zugriff auf verschachtelte DOM-Elemente. Diese Vorteile haben aber auch ihre Schattenseiten. So wird gerade wegen der umfangreichen Funktionalität die Ausführungszeit für einfachste Zugriffe stark ausgebremst. Das ist bei einem leistungsstarken Desktopbrowser zwar nicht unbedingt spürbar, macht sich aber bei mobilen Geräten bemerkbar.

Zur Abhilfe gibt es einige Alternativen. Die schnellste Lösung wäre, die nativen JavaScript-Funktionen direkt zu nutzen, ohne auf ein bestimmtes Framework zu setzen. Möchte man nicht ganz auf diesen Komfort verzichten, gibt es das Intel-App-Framework (ehemals jQ.Mobi), ein Open-Source-UI-Framework, das einen Mobile-optimierten Query-Selector bietet. Die Syntax ist der von jQuery sehr ähnlich. Erst ein Benchmark der Ausführungsgeschwindigkeit gegenüber jQuery und Zepto zeigt, wie langsam jQuery tatsächlich ist.

Aus Architektursicht wäre das Open-Source-MVC-Framework AngularJS eine interessante Möglichkeit. Dieses beinhaltet ebenfalls einen leichtgewichtigen Query-Selector mit dem Namen jqLite. Ausgesprochen wird es jQuery Lite, was aber nichts mit dem originalen jQuery zu tun hat. Die Performance von jqLite ist zwar nur an wenigen Stellen etwas schneller, allerdings setzt AngularJS auch auf ein ganz anderes Prinzip. Anstatt direkt auf den DOM mit jqLite zuzugreifen, gibt es eigene Funktionen, die diesen Vorgang nativ erfüllen: die Direktiven. Als Regel bei AngularJS gilt, niemals auf den DOM zuzugreifen, außer von Direktiven aus. Damit bringt AngularJS nicht nur ordentlich Geschwindigkeit, sondern eine saubere Architekturlösung mit sich. Gerade der Cross-Plattform- Entwickler sollte sich auf jeden Fall intensiver damit beschäftigen.

Tipp 4: Vermeide JavaScript-Animationen

Neben dem regulären Query-Selector von jQuery dürften die meisten Webentwickler folgende Funktion bereits benutzt haben: $(..).animate(..);.

Wieso auch nicht? Eine Animation ist damit sehr einfach und schnell hinterlegt und sieht auch noch gut aus. Hier kommen wir aber zur nächsten jQuery-Falle. Denn die Animationen werden über die CPU berechnet. Auf mobilen Geräten fühlt sich die Animation daher oft nicht flüssig an. Werfen wir einen Blick unter die Haube der Browser, um ihr Verhalten besser verstehen zu können.

Moderne Browser haben zwei wichtige Threads am Laufen, jeder hat seinen eigenen Aufgabenbereich und zum Rendern der Webseite arbeiten sie zusammen. Der erste Thread ist der Main-Thread. Dieser führt den JavaScript-Code aus, berechnet die HTML-Elemente gemeinsam mit den CSS Styles und ist anschließend für das Layouten der Seite sowie das Verarbeiten von Elementen in Bitmaps zuständig. Diese werden dem zweiten Thread überreicht: dem Compositor-Thread, der wiederum die Bitmaps mittels GPU zeichnet. Ist er fertig, wird eine Updateanfrage an den Main-Thread gestellt, um die Sichtbarkeit der Elemente zu prüfen. Der Compositor-Thread ermittelt auch, welcher Part aktuell sichtbar ist, und welcher beim Scrollen sichtbar werden soll. Außerdem berechnet er die Bewegung der Elemente beim Scrollen.

Der Main-Thread ist beim Ausführen von JavaScript-Code oder beim Rendern großer Elemente nicht ansprechbar. Das hat zur Folge, dass in dieser Zeit keine Benutzereingaben ankommen. Der Compositor-Thread entgegen ist sehr flexibel für Benutzereingaben. Er zeichnet den Webseiteninhalt circa 60 Mal pro Sekunde.

Wenn zum Beispiel der Benutzer die Webseite scrollt, fragt der Compositor-Thread den Main-Thread nach einem Update der neu gezeichneten Inhalte. Reagiert der Main-Thread nicht, wartet der Compositor-Thread nicht und zeichnet den nächsten Part des Inhalts weiter; der Rest wird als weiß mitgegeben. Ganz nach dem Fire-and-Forget Pattern.

Zurück zur jQuery-Animation. Diese berechnet mittels JavaScript die neuen Positionswerte für das DOM-Element. Nach jeder Veränderung muss eine Absprache zum Compositor-Thread stattfinden. Damit ist der Main-Thread bei jeder kleinsten Bewegung der Animation wieder an der Reihe und wird stark belastet.

Die Lösung für das Problem sind CSS-Transitions. Eine Animation wird in diesem Fall einmalig vom Compositor-Thread übernommen und somit auch von der GPU-Hardware beschleunigt verarbeitet. Listing 2 zeigt, wie eine CSS-Transition zum Drehen eines div-Elements geschrieben wird. Diese Art von Animationen laufen flüssig auf dem mobilen Gerät, so wie es der Benutzer erwartet.

Das ist übrigens der Grund, weshalb native Apps oft schneller als Web-Apps reagieren. Die Oberfläche wird hier meist komplett über die GPU gerendert, bei Web-Apps ist man vom Code und dessen Browser-Engine abhängig.

Listing 2: Mit CSS-Transitions die Hardwarebeschleunigung nutzen
<!DOCTYPE html>
<html>

  <head>
    <title>CSS Transitions</title>
    <style>
      div {
        width: 300px;
        height: 100px;
        background-color: red;
        border: 1px solid black;
      }
      
      div:hover {
        -webkit-transform: rotateY(150deg);
        transform: rotateY(150deg);
        transition: all .3s ease-out;
      }
    </style>
  </head>

  <body>
    <div>I love GPU!</div>
  </body>
</html>

Tipp 5: Touchverzögerung entfernen

Als große Kritik an Hybrid-Apps hört man immer wieder die langsame Verarbeitung der Benutzereingaben. Bei einem Klick- oder Touchvorgang findet eine Verzögerung von 300 ms statt. Woran liegt dieser Effekt?

Das große Geheimnis dahinter ist, dass der Browser darauf wartet, ob wiederholt ein Klick oder Touch stattfindet, sodass er diesen als Doppelklick oder Doppeltap verarbeiten kann. Hierfür gibt es mit dem Open-Source-Projekt FastClick eine Rundumlösung. Die JavaScript Library funktioniert mit allen gängigen Browsern und deaktiviert die unerwünschte Zeitverzögerung. Die Verwendung ist leichtgewichtig. Um die Zeitverzögerung zu deaktivieren, muss man lediglich eine FastClick-Instanz erzeugen und das fertig gerenderte Dokument überreichen. Eine FastClick-Demoseite für den mobilen Browser verdeutlicht noch einmal den Unterschied. Abbildung 7 zeigt die klassische Verarbeitung der Toucheingabe, Abbildung 8 den Einsatz von FastClick.

Listing 3: Die Zeitverzögerung von Benutzereingaben deaktivieren mit FastClick
<script type='application/javascript' src='fastclick.js'></script>
<script type='application/javascript'>
  window.addEventListener('load', function() {
    new FastClick(document.body);
  }, false);
</script>
Abb. 7: Die Toucheingabe wird mit 300 ms verzögert ausgeführt

Abb. 7: Die Toucheingabe wird mit 300 ms verzögert ausgeführt

Abb. 8: Zeitverzögerung der Toucheingabe durch Einsatz von FastClick deaktiviert

Abb. 8: Zeitverzögerung der Toucheingabe durch Einsatz von FastClick deaktiviert

Fazit

Die Top-5-Performancetipps haben gezeigt, dass einiges an Geschwindigkeit herausgeholt werden kann. Die Kritik, dass Web sei zu langsam, ist nicht gerechtfertigt. Eher sind es einige Fallen, die auf den Webentwickler lauern, in die er viel zu schnell hineintappt. Wir haben gesehen, wie sich der Download des HTTP-Protokolls verhält, dass es jQuery faustdick hinter den Ohren hat und wie der Browser das Rendern von Inhalten hinter den Kulissen handhabt. Ein schlechtes Image wurde ebenfalls durch eine Zeitverzögerung verliehen, was jedoch nicht gerechtfertigt ist. Durch eine Zeile Code konnte das Problem elegant umgangen werden. Sie haben jetzt einige wichtige Profitipps gelernt und Ihr Weg für erfolgreiche Hybrid-Apps ist gelegt. Ich wünsche viel Spaß beim Ausprobieren und Optimieren. Bei Fragen stehe ich gerne jederzeit per E-Mail oder über Social Networks zur Verfügung.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -