JavaFX: One codebase to rule them all!

Keine Kommentare

Plattformübergreifende mobile Applikationen mit JavaFX

jd_run_anywhere

Mit Veröffentlichung der ersten Version von JavaFX (2+) führte sogleich „going mobile“ die Hitlisten der am meisten gewünschten Features an. Die Vorstellung, auf einer Codebasis Applikationen für diverse Plattformen bereitstellen zu können (das berühmte WORA Paradigma – Write Once Run Anywhere) erscheint schon sehr verlockend.

Auf der JavaOne 2013 hat Oracle prototypisch gezeigt, dass es möglich ist, JavaFX-basierte Applikationen auf mobile Geräte, auf denen Android und iOS läuft, zu portieren. Danach zog sich Oracle aus dem Thema offiziell zurück und übergab den Code der Community.

Das daraufhin Ende 2013 von Johan Vos gestartete Projekt JavaFXPorts hatte es sich zunächst zur Aufgabe gemacht, JavaFX basierend auf OpenJFX nach Android zu portieren und gewann auf der JavaOne 2014 den Duke’s Choice Award.

Im Februar 2015 nun taten sich Johan’s Firma LodgOn (JavaFXPorts, JavaFX -> Android) und Trillian Mobile (das Unternehmen hinter RoboVM, JavaFX -> iOS) zusammen, um gemeinsam das Gradle JavaFX Plugin „javafxmobile-plugin“ zu veröffentlichen.

Dieses Plugin adressiert die drei Plattformen Desktop, Android und iOS soweit möglich auf einer gemeinsamen Codebasis. Wenn nötig, werden Plattformspezifika durch native Services ergänzt. Mit javafxmobile-plugin kann somit ein gemeisamens Projekt für alle Zielplattformen erstellt werden, aus dem haraus auf die jeweilige Plattform „deployed“ wird.

„javafxmobile-plugin“

Bietet Oracle bereits Java für Windows, Linux, Mac OS und ARM, so bietet JavaFXPorts einen Rahmen für JavaFX-Community-SDKs mit Fokus auf iOS und Android. Dabei spielt ein entscheidender Unterschied eine Rolle: iOS- und Android-Programme werden zu nativen Applikationen konvertiert, so dass diese auf gleicher Low-Level-Ebene agieren wie „normale“ mobile Anwendungen.

Es wird also keine Laufzeitumgebung mitgeliefert, in der Java-Bytecode interpretiert wird, sondern es werden Apple-AppStore- und Google-Play-Store-fähige Pakete erstellt. Dabei kommt für eine iOS-Applikation RoboVM und Xcode in Verbindung mit einem iOS-Developer-Account zum Tragen, für Android wird das Android SDK benötigt (Getting Started with JavaFXPorts).

Projektstruktur

Die Projektstruktur sieht per Konvention folgendermaßen aus:
jd_javafxports_project_structure

Plattformunabhängig (main)

src/main/java Allgemeiner Java-Code

src/main/resources Allgemeine Ressourcen

Android (android)

src/android/java Android-Java-Code

src/android/res Android-Assets

src/android/resources Android-Ressourcen

iOS (ios)

src/ios/java iOS-Java-Code

src/ios/resources iOS-Ressourcen

src/ios/assets iOS-Assets

Desktop (desktop)

src/desktop/java Desktop-Java-Code

src/desktop/resources Desktop-Ressourcen

Ressourcen und Assets?
src/…/resources werden automatisch zum Classpath hinzugefügt.
src/…/res/ src/…/assets werden wie plattformspezifische Ressourcen behandelt.

Gradle Tasks

Als Build-Tool kommt bei JavaFXPorts Gradle zum Einsatz, und es wird eine Anzahl an plattformspezifischen Build-Tasks bereitgestellt:jd_javafxports_gradle_tasks

Desktop

run startet die Applikation in der Desktop-Umgebung

Android

android generiert ein Android „apk“ im Verzeichnis build/javafxports/android

androidInstall installiert das generierte „apk“ auf ein angeschlossenes Android-Gerät

iOS

ios generiert ein iOS „ipa“ im Verzeichnis build/javafxports/ios

launchIOSDevice startet (und installiert) die Applikation auf einem angeschlossenen iOS-Gerät

launchIPhoneSimulator startet die Applikation in einem iPhone-Simulator

launchIPadSimulator startet die Applikation in einem iPad-Simulator

Gluon

Vor einiger Zeit startete ein neues Projekt: Gluon mit dem Ziel, die Welten Desktop, Android und iOS in einer Projektstruktur zu vereinen und rund um das Thema „Entwicklung plattformübergreifender Applikationen“ Frameworks und Services anzubieten. Dabei sollen nach und nach Plugins für die gängigsten IDEs bereitgestellt werden. Derzeit ist ein Gluon-Plugin für NetBeans verfügbar, das über das NetBeans-Plugins-Portal sehr einfach installiert werden kann.

Nach der Installation des Plugins steht für neue JavaFX-Projekte das Template „Basic Gluon Application“ zur Verfügung, das eine Gluon-, bzw. JavaFXPorts- Projektstruktur erstellt:

Benötigt die neue Applikation keinerlei gerätespezifische Zugriffe, kann der gesamte Code im gemeinsamen Bereich abgelegt und eine reguläre JavaFX-Anwendung erstellt werden.

In der allermeisten Fällen wird dies jedoch nicht ausreichen. Bereits beim Zugriff auf das Dateisystem wird es spezifisch (aus Gluon Charm Down):

Desktop

public File getPrivateStorage() {
  String home = System.getProperty("user.home");
  File f = new File(home, ".gluon");
  if (!f.isDirectory()) {
    f.mkdirs();
  }
  return f;
}

Android

public File getPrivateStorage() {
  return FXActivity.getInstance().getFilesDir();
}

iOS

public File getPrivateStorage() {
  NSFileManager fm = NSFileManager.getDefaultManager();
  NSURL libdir = fm.getURLForDirectory(
   NSSearchPathDirectory.LibraryDirectory,
   NSSearchPathDomainMask.UserDomainMask, null, false);
  File appPrivateDir = new File(libdir.getPath() + "/Caches/gluon");
  if (!appPrivateDir.exists()) {
    appPrivateDir.mkdirs();
  }
  return appPrivateDir;
}

Auch für den Zugriff auf weitere Ressourcen und Dienste des Gerätes oder der Plattform wie Netzwerk/Internet, Adressbuch, Kalender, Audio/Video, GPS, Kamera etc. wird man auf native Implementierungen zugreifen.

Dabei ist es aus Entwicklersicht sicher wünschenswert, wenn sich der Zugriff auf gerätespezifische Ressourcen möglichst transparent und einheitlich gestaltet.

Gluon Charm Down

Dieses Ziel hat sich die vor Kurzem in der Version 0.0.1 freigegebene OSS-Zusatzbibliothek Gluon Charm Down gesetzt.

Damit kann dann via

PlatformFactory.getPlatform()

die für die jeweilige Platform die passende Implementierung einer „Platform“ angefordert werden.
Somit bekommt man durch

  File path = PlatformFactory.getPlatform().getPrivateStorage();

den jeweils gültigen Dateipfad für applikationspezifische Dateien.
Ähnliches gilt zum Beispiel auch für den Anwendungsstatus. Charm Down sieht hier eine CallBack-Schnittstelle vor, die es ermöglicht, auf die LifecycleEvents START, PAUSE, RESUME, STOP zu reagieren, zum Beispiel:

public class IosPlatformProvider implements PlatformProvider {
 
    private final BooleanProperty stop = new SimpleBooleanProperty();
    private final BooleanProperty pause = new SimpleBooleanProperty();
 
    {
        PlatformFactory.getPlatform().setOnLifecycleEvent((LifecycleEvent param) -> {
            switch (param) {
                case START:
                    pause.set(false);
                    stop.set(false);
                    break;
                case PAUSE:
                    pause.set(true);
                    break;
                case RESUME:
                    pause.set(false);
                    stop.set(false);
                    break;
                case STOP:
                    stop.set(true);
                    break;
            }
            return null;
        });
    }
 
    @Override
    public BooleanProperty stopProperty() {
        return stop;
    }
 
    @Override
    public BooleanProperty pauseProperty() {
        return pause;
    }
}

Und das funktioniert wirklich?

Ja.

Es ist sicherlich noch einiges zu tun, aber zumindest hat der derzeitige Stand der Tools für José Perada und mich gereicht, um das JavaFX-Spiel 2048FX von Bruno Borges fit zu machen für Google Play (Android) und den Apple Store (iOS).

Die in den App Stores veröffentlichte Version liegt quelloffen bei Github.

2048FX_GooglePlay    2048FX_AppStore

 

 

 

Jens Deters

Etwa 25 Jahre ist es her, dass Jens Deters mit dem Home-Computing begonnen hat. Damals zogen ihn Computer in ihren Bann, und die letzten 15 Jahren hatte er verschiedenste Rollen im IT- und Telekommunikationsumfeld inne (Software Entwickler, Trainer, Berater, Projekt- und Produktmanager). Heute arbeitet er bei codecentric als Senior IT Consultant. Er schreibt regelmäßig über seine Projekte (www.jensd.de, www.mqttfx.org) und trägt zur JavaFX- und IoT-Community bei. Jens ist Mitglied des NetBeans Dream Teams.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.