JFXMobile – first attempt

Who would not like to be able to write a single code base for desktop and mobile? I know I want to, and the applet I’m using for time registration is getting into a pinch with all the browsers dropping support for applets, so why not give JavaFX a try? And see if things go as smoothly as Gluon’s tweets make it sound?

HelloWorld
So, first things first and setup a nice virtual machine for this project with Java and Eclipse, hookup the old 1st gen Nexus 7, then download the HelloWorld demo project from Gluon. Finally a “gradlew androidInstall” should do the trick…

But no such luck!

Execution failed for task ':dex'.
> org.gradle.api.GradleException (no error message)

Dex fails and without an error message. Great! But luckily --debug shines a light on the problem: dex is started with a default of Xmx=2g, and I’m still in the habit of installing 32 bit Java, even if I run a 64 bit operating system. And 2g for a single process is a problem then.

But what is the best solution? Either 64 bit Java needs to be installed, and I often run into issues with that, or dex needs to be convinced to run with a lower Xmx. Some researching into Gluon’s JFXMobile plugin reveals that the android block has a subblock called dexOptions, where the javaMaxHeapSize can be defined; and 1024m works just fine.

jfxmobile_nexus7

And look at that: one HelloWorld JavaFX application running on my Nexus 7 Android tablet! Startup time ain’t bad at all either.

JFXtras
Next step: including JFXtras. The application I intend to write needs a date picker and being the guy behind JFXtras, well, I of course want to use my own library. The dependency is added quickly to the build.gradle file, the HelloWorld application quickly changed to add a CalendarPicker next to the Label, and redeploy!

com.android.dx.cf.iface.ParseException: InvokeDynamic not supported

Que??? Oh… Right… Android uses Java 7, and JFXtras 8.0 uses Java 8, including Lambda’s.
I forgot.

RetroLambda
It would be an option to use JFXtras 2.x, which is Java 7 based, but it is so old, I really want to use the 8.0 branch.

Luckily there is a tool called RetroLambda, which apparently allows backporting of lambda’s to Java 7. It’s an easy include in the build.gradle script, so let’s give it a try. 

Compile is still ok, good, good, and…

com.android.dx.cf.iface.ParseException: InvokeDynamic not supported

Eh??? Ah… RetroLambda’s only processes the active project, being the HelloWorld application, not dependencies like JFXtras. So JFXtras needs to be RetroLambda-ed as well. Hm. Thread carefully there, I’m not the only one using it. So let’s just install that 8.0-R5-SNAPSHOT in the local Maven repository only.

JFXtras compiling is ok… Let’s see if JFXtras samples runs under Windows…

default method found in version 50.0 classfile

Ah. Yeah. It would have been too good to be true. RetroLambda is actually generating Java 6 classfiles, but those can’t handle default methods in interfaces used a.o. by JFXtras’ Agenda.

But again RetroLambda might offer a solution: it has a rudimentary implementation for default methods, activated by:

retrolambda {
defaultMethods=true
}

Compile is ok… Running samples… Hey! That actually seems to work! Samples are coming up. Interesting! 

Android
Alright! Let’s try and build the HelloWorld on Android again.

jfxmobile_nexus7picker

I’ll be damned! It works! Those month and year ListSpinners need to have their arrows moved to opposing sides (with the -fxx-arrow-position CSS property), but for the rest it looks quite ok.

Let’s click a bit… Oh… That is sluggish. The time between the touching of the screen and the calendar picker actually responding is almost 2 seconds. I know my aging Nexus 7 isn’t the fastest, but… No… Actually that seems accurate for other apps as well. I need a new hardware.

Conclusion
That was not too hard at all. I probably stumbled into everything that reasonably can go wrong, but I do that all the time, being a bug magnet and all. The performance is a bit of a concern, let’s see what a newer tablet does.

What really worries me is the RetroLamda. Don’t get me wrong, it’s a great tool, but having to postprocess Java 8 files is not very promising for the future… What will happen when Java 9 with JigSaw comes out in 2016? Oracle and Google really need to get their heads out of each other’s b*tts and stop blocking progress.

For Oracle having the penetration on mobile hardware that Android has, is just free ‘presence’, so stop wining about it. Android and Java were was already in place before you became the boss of Java. Use the situation to your advantage, instead of frustrating the ecosystem.

OTOH Google seems a bit short sighted that it won’t let Oracle be part of that ecosystem by compromising. Open source is not for everyone, and having to switch to some other programming platform besides the JVM (class files need not be written in Java after all) is in many ways a step back. I, for one, would not mind something like a 1% fee on my Android hardware, in order to get regular updates of official Java on Android. Or a pay-per-update (of the JVM) model. It should be possible to have separate updates of the JVM available for apps. Some companies simply have different earning models, there must be a common ground somewhere.

Anyhow. To be continued…

This Post Has 13 Comments

  1. Montechristos

    Hi! I am also new to Gluon and also stuck…

    I am getting the following error:
    > Failed to apply plugin [id ‘org.javafxports.jfxmobile’]
    > Could not get unknown property ‘compileRetrolambda’ for task set.

    How did you resolve it? I am not using JFExtras, so I guess it is a problem with retrolambda?

    Thanks in advance

    1. rwijngaa

      Well, my problem was with jfxtras-labs. If i remember correctly, i checked out the jfxtras-labs source and (retro) compiled it to java 7. After that i ued that (snapshot) version in my project and i could use the Gauge.
      Hope this helps.

    2. tbeernot

      Since I never got that error, I don’t know how I solved it. But the first error explains the second: if it cannot apply the plugin, the property won’t be present. So the question is: why can it apply the jfxmobile plugin? Have you added jcenter as a repository and jfxmobile as a dependencies as described on http://docs.gluonhq.com/javafxports/#_getting_started

  2. Igor Rybak

    Hello, thanks for article. It works but I found some issue. If build for example some complex node hierarchy:
    http://vk.com/doc234728096_437208138
    and programmably add ListSpinner
    public class MainSceneController implements Initializable {
    @FXML
    public HBox contHbox;
    @Override
    public void initialize(URL location, ResourceBundle resources) {
    ListSpinner listSpinner=new ListSpinner(0,100);
    listSpinner.setPrefWidth(80);
    listSpinner.setValue(10);
    contHbox.getChildren().addAll(listSpinner);
    }
    }
    than running on android device, when I click on listspinner the application crashes.
    Adb logcat repots:
    01-19 13:59:43.650 24709-24735/com.gluonapplication E/AndroidRuntime﹕ FATAL EXCEPTION: JavaFX Application Thread
    java.lang.StackOverflowError
    at javafx.scene.layout.Region.impl_computeGeomBounds(Region.java:3078)
    at javafx.scene.Node.updateGeomBounds(Node.java:3556)
    at javafx.scene.Node.getGeomBounds(Node.java:3509)

    it works fine on desktop or using more simple hierarchy. Is it possible to fix?

    1. tbeernot

      I’m not sure what I can fix. ListSpinner is working on the desktop, so is it Android that is goofing up?

  3. tbeernot

    Android apparently will be picking op OpenJDK, so things may change in the future. But invokeDynamic is a Java 7 feature not 8. I’ll give a gauge a try as well. (BTW, I’ve split off the gauges in their own jar last week.)

  4. rwijngaa

    That was the whole point using retrolambda right? TMN there are no Java 8 versions of android 😉
    I will give other controls a try. Thanks

  5. tbeernot

    Well, no, invokeDynamic is a Java 7 feature and apparently it is not supported on the Android you are using. It has nothing to do with JFXtras. I know for sure you can use the CalendarPicker, so give that a try.

  6. rwijngaa

    Thanks. This works for simple examples. But as soon as i use something from the jfxtras-labs (a BasicRoundDailGauge for example) you’ll get “InvokeDynamic not supported”. I guess the jfxtras-labs is too alpha?

  7. rwijngaa

    Hi, thanks for your blog article!. I followed along (with a basic gluon app) and got ” Could not find property ‘compileRetrolambda’ on task set”. I’m kind of a newbie on gradle, so i was wondering if you could share the build.gradle file?
    Thanks

    1. tbeernot

      Sure, but what build file do you want. JFXtras is on github, so I assume the one for the mobile JavaFX project…

      buildscript {
      repositories {
      jcenter()
      }
      dependencies {
      classpath ‘org.javafxports:jfxmobile-plugin:1.0.0-b10’
      classpath ‘me.tatarka:gradle-retrolambda:3.2.2’
      }
      }

      apply plugin: ‘org.javafxports.jfxmobile’
      apply plugin: ‘me.tatarka.retrolambda’

      repositories {
      mavenLocal()
      mavenCentral()
      jcenter()
      maven { url ‘http://nexus.gluonhq.com/nexus/content/repositories/releases/’ }
      maven { url “http://oss.sonatype.org/content/groups/public/” }
      }

      dependencies {
      compile ‘com.gluonhq:charm:0.0.2’
      compile ‘org.jfxtras:jfxtras-controls:8.0-r5-SNAPSHOT’

      androidRuntime ‘com.gluonhq:charm-android:0.0.2’
      iosRuntime ‘com.gluonhq:charm-ios:0.0.2’
      desktopRuntime ‘com.gluonhq:charm-desktop:0.0.2’
      }

      mainClassName = ‘com.helloworld.HelloWorld’

      jfxmobile {
      android {
      manifest = ‘src/android/AndroidManifest.xml’
      dexOptions {
      javaMaxHeapSize = ‘1024m’
      }
      }
      ios {
      infoPList = file(‘src/ios/Default-Info.plist’)
      }
      }

      By now the Gluon versions all have increased to 1.0.0

Leave a Reply to tbeernot Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.