Windows 8 + Windows Phone 8 Cross Platform App Development mit C#/XAML (Teil 1)
Ich hab zuletzt ein paar Vorträge zum Thema W8/WP8 Cross Platform Development gehalten. Ich nutze hier die Gelegenheit eine schriftliche, nachlesbare Variante des Vortrags abzuliefern. Die Folien zum Vortrag findet Ihr hier.
Grundsätzlich ist das Ansinnen möglichst viel Code wiederverwenden zu wollen absolut nachvollziehbar. Wenn wir einen Blick auf die – inzwischen wohl hinlänglich bekannten – Architekturdiagramme der beiden Betriebssysteme werfen, werden wir feststellen, dass es durchaus Bereiche gibt, die gleiche oder ähnliche Namen tragen und demzufolge doch Hoffnungen wecken, dass wir uns hier – für den Fall, dass wir eine App für WP8 als auch W8 entwickeln wollen – Arbeit sparen können. Und hier ist gleich ein ganz wichtiger Punkt festzuhalten: Wir müssen – da führt kein Weg vorbei – zwei separate Apps entwickeln. W8 und WP8 basieren zwar auf dem gleichen Betriebssystemkernel (was einige nette Nebeneffekte hat). Dennoch sind beides erst mal getrennte Betriebssysteme, die sich während der Entwicklung von Apps aber erstaunlich ähnlich anfühlen können. Unsere Aufgabe ist es also herauszufinden, wo diese Ähnlichkeiten liegen, um diese dann geschickt auszunutzen.
Für diesen Blogpost konzentriere ich mich auf den C#/XAML/Windows (Phone) Runtime Bereich in den Diagrammen. Für Cross-Plattform Entwicklung eignet sich grundsätzlich auch C++, in diesem Post spare ich dieses Thema allerdings aus. Man mag sich fragen, weshalb JS/HTML im Phone Diagramm gar nicht gelistet ist, wo es doch sogar Templates für WP8 App Entwicklung in Visual Studio für JS/HTML gibt. Der Hintergrund dafür ist, dass wir mit JS/HTML auf WP8 keine “nativen” Apps schreiben können, sondern diese immer in einem Webcontrol gehostet sein müssen. Bei W8 ist das anders, hier brauchen wir keinen Container, in dem das JS läuft, wir können direkt aus JS auf die (nativen) WinRT APIs zugreifen.
Verwirrend? Vielleicht! Einmal verstanden, weiß man aber, was geht und was nicht. Natürlich wäre es aus Entwicklersicht wünschenswert, wenn hier eine Vereinheitlichung der Programmiermodelle möglich wäre. Ich denke , dass wir mit C#/XAML wie ich es hier erläutern werde schon einen ordentlichen Weg einschlagen.
Generelle Strategie
Bevor wir in die Details gehen, sollten wir uns überlegen, was technisch nicht nur machbar, sondern auch sinnvoll ist. Wo lohnt es sich tatsächlich Elemente wiederzuverwenden? Die Antwort ist recht einfach: Überall da, wo wir nicht an die Benutzeroberfläche gebunden sind. Die Benutzeroberfläche für Windows Phone und Windows 8 sieht – bedingt durch die Kacheln - zunächst sehr ähnlich aus. Bestimmte Bedienelemente erkennt man als Anwender auch sehr leicht wieder, so dass sich ein WP Anwender auf W8 recht schnell heimisch fühlt und umgekehrt. Ein Beispiel hierfür sind die Live-Tiles. Dennoch gibt es auch Unterschiede, die wir respektieren müssen. So gibt es – sicherlich nicht zuletzt bedingt durch die Bildschirmgröße - unterschiedliche UI Controls, die für die Bedienung auf den jeweiligen Geräten optimiert sind. Um viele gruppierte Datensätze anzuzeigen gibt es auf dem Windows Phone das Pivot Control. Auf Windows 8 existiert dagegen das Grid View mit der Möglichkeit Semantic Zoom zu nutzen. Wir sollten als Entwickler trotz der Unterschiede dem Anwender die optimale Bedienung erlauben. Eine Windows Phone App ist nicht einfach nur eine geschrumpfte Windows Store App. Mit diesem Wissen folgt die Erkenntnis, dass wir, was die UI Gestaltung angeht, nicht viel Spielraum haben Code 1:1 wiederzuverwenden. Es gibt ein paar Hacks, die es erlauben eigene UI Controls für beide Plattformen zu erstellen oder XAML Dateien wiederzuverwenden. Meine Empfehlung ist an dieser Stelle etwas radikaler: Tut es nicht! Versucht besser die Bedienung Eurer App für das jeweilige Endgerät zu optimieren. Es gibt ja noch andere Ecken und Enden, an denen wir durch Wiederverwendung profitieren können. Im Zuge der Benutzerschnittselle stehen wir dennoch nicht mit leeren Händen da: Unser Wissen über XAML wird uns auf beiden Plattformen weiterhelfen.
Abkoppeln von nicht wiederverwendbaren Bereichen und MVVM
Mit diesem Wissen sollten wir versuchen nichtwiederverwendbare Teile von den wiederverwendbaren Teilen abzukoppeln. Beim UI gelingt das über Ansätze wie MVVM recht gut. Die View ist danach losgelöst von jeglicher Logik und kann – abhängig von der Zielplattform, neu implementiert und an das ViewModel gebunden werden. Auch andere Bereiche werden nicht oder nur schwer wiederverwendbar sein. So gibt es zwar ähnliche aber nicht identische Implementierungen des Process LifeCycle. Bei WP8 haben wir die Status Dormant und Tombstoned, bei W8 Suspended und Terminated. Die Konzepte sind ähnlich , es gibt aber Unterschiede. Statt hier auf Wiederverwendung von Code zu pochen, kümmern wir uns erst mal um das, was wir tatsächlich wiederverwenden können: Einen großen Teil unserer Anwendungslogik.
Portable Class Library
Am angenehmsten wäre für uns als Entwickler die Arbeit, wenn wir den Logik-Code einmalig schreiben, einmalig kompilieren würden und das daraus resultierende Binary einfach auf beiden Plattformen läuft. Good news: Das funktioniert, wenn auch mit Einschränkungen. In den Visual Studio 2012 Versionen ab Visual Studio Professional aufwärts gibt es eine Projektvorlage “Portable Class Library”. Wenn wir diese auswählen, können wir Zielplattformen anwählen wie Windows Phone 8, Windows Store Apps und Xbox. Durch das Setzen von Häkchen können wir dafür sorgen, dass der Build-Output des Projekts binär kompatibel zu allen ausgewählten Zielplattformen ist. Das klingt fast zu schön um wahr zu sein, oder? Ok, es gibt eine Einschränkung, die wir verstehen müssen. Die Portable Class Library arbeitet nach dem Prinzip des kleinsten gemeinsamen Nenners im .Net Framework. Am besten ist das zu verstehen, wenn wir uns das .Net Framework als große Menge von Namespaces und Klassen vorstellen.
Wie wir vielleicht bereits wissen, ist auf dem Windows Phone nicht das komplette .Net Framework zur Verfügung, sondern lediglich eine Submenge, die auf die Anwendung auf dem Phone zugeschnitten ist (niemand braucht ASP.NET auf dem Phone, oder? Oder vielleicht doch? Ok – wir müssen mit dem Leben, was hier verfügbar ist.) Diese Untermenge nennt sich “Profil”. Ebenso gibt es ein Profil, das auf die Windows Store Apps zugeschnitten ist – das “.NET Profile for Windows Store Apps”. Beide Profile unterscheiden sich, sind Submengen des vollen .Net Profils, sind aber nicht deckungsgleich. Dennoch gibt es Schnittmengen. Ebenso verhält es sich mit den Profilen für die anderen auswählbaren Zielplattformen. Mit jedem Häckchen, das wir setzen reduzieren wir die Menge der zur Verfügung stehenden Namespaces auf die Schnittmenge aller Profile – und damit auf den kleinsten gemeinsamen Nenner. Die Entscheidung, welche Plattformen wir unterstützen wollen, sollten wir also sehr bewusst treffen, ein nachträgliches Aufnehmen einer weiteren Plattform wird – abhängig von der Aufgabe der Library – nur schwierig möglich sein.
Struktur in der Solution
Wenn wir uns jetzt eine Solution im Visual Studio aufbauen, sollten wir darauf achten, dass die Struktur noch verständlich bleibt. Ich lege daher immer mehrere Ordner an, deren Inhalt durch die Namensgebung schon klar sein sollte:
W8: Ein Ordner, in dem ich später das Windows Store App Projekt ablege und weitere Projekte und Libraries, die ausschließlich für diese App relevant sind.
WP8: Ein Ordner, in dem ich später das Windows Phone App Projekt ablege und alle zugehörigen Projekte und Libraries, die ausschließlich für WP relevant sind.
SharedLib: Hier lege ich meine Portable Library Projekte ab.
SharedCode: Hier lege ich später einzelne Code Files ab, die identisch in mehreren Projekten verwendet werden können – dazu später mehr.
Wenn wir jetzt anfangen Code zu schreiben, können wir versuchen möglichst viel in die Portable Library zu packen. Wenn ich hier alles reinpacken kann, habe ich tatsächlich sehr viel Arbeit gespart. Ich muss dann lediglich meine beiden Projekte (für WP8 und W8) anlegen und kann die Portable Libs referenzieren. Wenn ich dann noch jeweils ein eigenes UI für meine Anwendungen erstelle, bin ich fast am Ziel. Allerdings sind wir in dem, was in Portable Libraries geschehen kann stark begrenzt, da ja, wie bereits erwähnt, mit jeder Zielplattform die zur Verfügung stehenden Namespaces weniger werden.
Warnung: An dieser Stelle sei eine kleine Warnung ausgesprochen: Das Vorhaben des Cross Platform Development wird sich wohl oder übel auf den Aufbau Eurer Solution und auf die Architektur auswirken. Überlegt gut, ob Ihr Eure Anwendungsarchitektur auf dem Altar des Cross Platform Development opfern wollt - und wenn ja, wie weit es sinnvoll ist, dieses Spiel zu treiben. Abhängig davon wie groß Euer Projekt ist, wie viele Entwickler Ihr seid, wie viel Erfahrung diese haben etc. müsst das zuletzt Ihr selbst entscheiden. Seid vorsichtig, lasst Euch beraten und sorgt vor allem dafür, dass Ihr selbst und Euer Team den Aufbau am Ende noch verstehen!
Spätestens, wenn wir auf die Windows (Phone) Runtime zugreifen wollen, werden wir feststellen, dass die Portable Lib meckert – wir sind ab diesem Zeitpunkt nicht mehr binär kompatibel. Da jedoch die Windows Runtime und die Windows Phone Runtime wiederum nicht absolut unterschiedlich, sondern ähnlich sind, können wir hier eventuell Code wiederverwenden – der aber zielplattformabhängig neu kompiliert werden muss. Die Namespaces der Windows Runtime und der Windows Phone Runtime überlappen sich. Wir haben also einen Bereich von Funktionalität, deren API auf WP8 genauso implementiert ist wie auf W8. Solange wir uns in diesem Bereich aufhalten, schreiben wir genau die gleichen Zeilen Code für W8 wie für WP8 um Windows Runtime bzw. Windows Phone Runtime Aufrufe zu machen. Was uns das bringt und inwieweit wir mit dieser Aufgabe umgehen können – dazu folgt ein Post in den nächsten Tagen. Ihr könnt ja schonmal in die Folien spitzen, was da noch so kommen mag…
Update 21.5.2013:
Comments
- Anonymous
April 07, 2013
Schöne Einführung in die Thematik und Augenöffner für das, was geht bzw. was nicht geht. Bin auf die nächsten Teile gespannt. Gut!