iOS-App-Entwicklung mit JavaFX

Eine JavaFX-App auf einem iPhone oder Android-Phone zum Laufen zu bringen, diese Idee ist nicht sehr neu. Bereits auf der JavaOne 2012 wurde von Oracle hierfür iPack vorgestellt. In meinem letzten Blog [2] bin ich auf die Technologie RoboVM eingegangen, mit der die Ausführung von Java und anderen JVM-Sprachen auf der iOS-Plattform ebenfalls möglich wird. Bei der Recherche zu iPack im Netz habe ich ziemlich bald festgestellt, dass auch iPack zur Bereitstellung der Anwendung für die verschiedenen Plattformen RoboVM verwendet. In diesem Blog möchte ich zeigen, ob und wie eine JavaFX-App mit RoboVM (ohne ein iPack-Projekt außenrum) für das iPhone entwickelt werden kann.

Nur kleine Anpassungen sind erforderlich

Zu Beginn habe ich für eine Machbarkeitsanalyse die von RoboVM bereitgestellte JavaFX-Beispiel-App robovm-sample-jfx-app verwendet, um diese mit RoboVM erfolgreich auf einem iOS-Gerät zu starten. Das war auch problemlos möglich, einzig in der pom.xml musste die aktuelle Version von RoboVM eingetragen werden. Nach dieser kleinen Anpassung lief meine App:

 

Nun wollte ich eine eigene JavaFX-Anwendung auf dem iOS-Gerät unter Nutzung dieser Technologie starten. Als Basis nahm ich eine nach dem Tutorial von Oracle geschriebene AddressBook-Desktop-Anwendung. Zuerst mussten für die kleineren Bildschirmgrößen Anpassungen vorgenommen werden. Diese App steht inklusive Sourcen in meinem GitHub-Repository JavaFX-for-iOS zum Download bereit.

Natürlich wird für die Tests neben einem solchen Projekt auch noch das passende Equipment sowie die entsprechende Software benötigt.

Voraussetzungen

Die System-Voraussetzungen sind: Ein Mac, JDK8, XCode inkl. XCode-Kommandozeilentools, RoboVM, Eclipse sowie folgende Plugins für Eclipse (vgl.[2]):

Zusätzlich werden folgende Plugins benötigt

Hat man all diese Voraussetzungen geschaffen, ist es wichtig, darauf zu achten, dass sich JavaFX im Classpath befindet . Dieser Befehl installiert die Bibliotheks-Datei jfxrt.jar in das lokale Maven -Repository:

 

mvn install:install-file -Dfile=<Pfad_zum_jfxrt.jar>/jfxrt.jar
-DgroupId=com.oracle.javafx \ -DartifactId=javafx -Dversion=2.2 -Dpackaging=jar

 

In der enthaltenen pom.xml-Datei werden die Abhängigkeiten zum jar, zum Maven Plugin für RoboVM und zum JavaFX Maven Plugin deklariert.

  <plugins>
    <plugin>
       <groupId>org.robovm</groupId>
       <artifactId>robovm-maven-plugin</artifactId>
       <version>{aktuelle_version_robovm-maven-plugin}version>
        [...]
    </plugin>
    <plugin>
       <groupId>com.zenjava</groupId>
       <artifactId>javafx-maven-plugin</artifactId>
        <version>{aktuelle_version_javafx-maven-plugin}version>
        </plugin>
     </plugins>
 
 <dependencies>
    <dependency>
         <groupId>com.oracle.javafx</groupId>
         <artifactId>javafx</artifactId>
         <version>{aktuelle_version_javafx}</version>
     </dependency>
      [...]
 </dependencies>

Die App kann nun mit Maven gebaut und gestartet werden. Je nachdem wo die Anwendung gestartet werden soll, ist folgender Befehl erforderlich:

  • Im Simulator: mvn robovm:iphone-sim
  • Auf einem angeschlossenen iOS-Gerät: mvn robovm:ios-device

Jetzt dauert es etwas – aber nach einer Weile sollte die Ausgabe so aussehen:

 

Doch Vorsicht: So leicht wie es sich liest , gestaltet sich der Nachtest nicht. Es sind diverse technische Stolperfallen enthalten.

Ohne spezielle „launcher class“ geht es nicht

Um eine JavaFX-App auf einem iOS-Gerät zu starten, ist ein spezieller Wrapper – eine „start“-Klasse (launcher class) erforderlich, die die JavaFX-App in die Cocoa Touch UI lädt. Diese zusätzliche Java-Klasse muss jeder JavaFX-Applikation hinzugefügt werden, wenn sie auf einem iOS-Gerät ausgeführt werden soll.

Das Maven Plugin wird in einer zukünftigen Version wahrscheinlich diese Klasse automatisch erzeugen [5]. In der AddressBook-App heißt die Java-Klasse RoboVMMain. Den Start der eigentlichen JavaFX-App übernimmt die Methode didFinishLaunching (mehr dazu in meinem Blog [2]). Mit dem Code:

public void run() {
Application.launch(JavaFXAddressBook.class);
}

ist es möglich, JavaFX-Komponenten in iOS-Anwendungen einzubinden.

public class RoboVMMain extends UIApplicationDelegateAdapter
{
    @Override
    public boolean didFinishLaunching(UIApplication application, NSDictionary launchOptions)
    {
        Thread launchThread = new Thread()
        {
            @Override
            public void run()
            { 
      
              Application.launch(JavaFXAddressBook.class);
            }
        };
        launchThread.setDaemon(true);
        launchThread.start();
        return true;
    }
    public static void main(String[] args) throws Exception
    {
        System.setProperty("glass.platform", "ios");
        System.setProperty("prism.text", "native");
        NSAutoreleasePool pool = new NSAutoreleasePool();
        UIApplication.main(args, null, RoboVMMain.class);
        pool.drain();
    }
}

Die eigentliche JavaFX-AddressBook-App (Java-Klasse: JavaFXAddressBook) enthält eine eigene main-Methode, von der aus die JavaFX-Anwendung gestartet wird („start“-Methode).

public class JavaFXAddressBook extends Application
{
    private TableView<AddressBookItem> table = new TableView<AddressBookItem>();
    public static void main(final String[] args) throws Exception
    {
        launch(args);
    }
    @Override
    public final void start(final Stage stage) throws Exception
    {
        Scene scene = new Scene(new Group());
        stage.setTitle("Address Book JavaFX");
        final Label label = new Label("Address Book");
        label.setFont(new Font("Arial", 20));
        // initialize AddressBookView
        BorderPane layout = createMainWindowLayout(label, table);
        
        ((Group) scene.getRoot()).getChildren().addAll(layout);
        stage.setScene(scene);
        stage.show();
    }
}

Fazit

Eine JavaFX -Anwendung lässt sich auf einfache Art und Weise auf einem iOS-Device ausführbar machen. Man kann vorhandene Java- / JavaFX-Kenntnisse einsetzen und die bereits existierenden Teile einer komplexen Anwendung hier einfach wiederverwenden. Doch wo Licht ist, ist auch Schatten. Und in diesem Fall überwiegen entscheidende Nachteile:

  • Die Oberfläche erinnert nicht mehr an eine native iOS-App und fügt sich somit nicht in das vom Benutzer gewohnte Look & Feel von iOS ein.
  • Eine JavaFX-Anwendung muss auch immer an die kleinen Bildschirmauflösungen angepasst werden.
  • Im Moment ist Debugging nicht möglich.
  • In Puncto Performance hat die App JavaFX-for-iOS die gleichen Nachteile wie die RoboVM App RoboVM-for-iOS (vgl. [2]); daran ändert auch nichts, dass JavaFX sein eigenes Rendering mitbringt.

Man muss abwarten, bis ein praxistaugliches Release von RoboVM veröffentlicht wird. Hoffentlich können bald die ersten Ergebnisse der iOS-Portierung im AppStore begutachtet werden. Meiner Meinung nach ist JavaFX im Mobile-Umfeld zukunftsträchtig. Allerdings müssten JavaFX-Apps genauso schnell wie native Apps geladen werden. Auch nach dem Laden müsste die Performance erheblich gesteigert werden und darf im Vergleich zu nativen Apps nicht abfallen.

Links:

[1] JavaFX Tutorial „Creating an Address Book with FXML”: http://docs.oracle.com/javase/8/javafx/fxml-tutorial/fxml_tutorial_intermediate.htm

[2] Blog „iOS RoboVM – eine Native Cross-Plattform": http://www.it-economics.de/en/blog/-/blogs/ios-robovm-%E2%80%93-eine-native-cross-plattform

[3] Ein funktionierendes Beispiel – JavaFX (OpenJFX) AddressBook-App for iOS using RoboVM: https://github.com/Kourtessia/JavaFX-for-iOS

[4] Hello Mobile World App: https://github.com/Kourtessia/robovm-sample-jfx-app

[5] JavaFX On IOS Using RoboVM And Maven: http://www.zenjava.com/

[6] Niklas Therning, Sven Efftinge: Unusual Ways to Create a Mobile App: http://www.infoq.com/articles/unusual-ways-to-create-a-mobile-app