How to build Open JavaFX for Android.

Here's a short recipe for baking JavaFX for Android dalvik. We will need just a few ingredients but each one requires special care. So let's get down to the business.

 Sources

The first ingredient is an open JavaFX repository. This should be piece of cake. As always there's a catch. You probably know that dalvik is jdk6 compatible  and also that certain APIs are missing comparing to good old java vm from Oracle.  Fortunately there is a repository which is a backport of regular OpenJFX to jdk7 and going from jdk7 to jdk6 is possible. The first thing to do is to clone or download the repository from https://bitbucket.org/narya/jfx78. Main page of the project says "It works in some cases" so we will presume that it will work in most cases :)
As I've said dalvik vm misses some APIs which would lead to a build failures. To get them use another compatibility repository which is available on GitHub https://github.com/robovm/robovm-jfx78-compat. Download the zip and unzip sources into jfx78/modules/base.
We need also a javafx binary stubs. Use jfxrt.jar from jdk8.
The last thing to download are freetype sources from http://freetype.org. These will be necessary for native font rendering.

Toolchain setup

I have to point out that these instructions were tested only on linux. I suppose they will work with minimal changes also on Mac OS. I also presume that you were able to build open JavaFX. That means all tools like ant, gradle, gcc and jdk8 have been installed and are working all right. In addition to this you will need to download and install jdk7, Android SDK and Android NDK for native code compilation.  Installing all of them will take some time. Don't forget to put them in your path.

export ANDROID_SDK=/opt/android-sdk-linux
export ANDROID_NDK=/opt/android-ndk-r9b
export JAVA_HOME=/opt/jdk1.7.0
export PATH=$JAVA_HOME/bin:$ANDROID_SDK/tools:$ANDROID_SDK/platform-tools:$ANDROID_NDK:$PATH

Freetype

Unzip freetype release sources first. We will have to cross compile them for arm. Firstly we will create a standalone toolchain for cross compiling installed in ~/work/ndk-standalone-19.
$ANDROID_NDK/build/tools/make-standalone-toolchain.sh  --platform=android-19 --install-dir=~/work/ndk-standalone-19

After the standalone toolchain has been created cross compile freetype with following script:

export TOOLCHAIN=~/work/freetype/ndk-standalone-19
export PATH=$TOOLCHAIN/bin:$PATH
export FREETYPE=`pwd`
./configure --host=arm-linux-androideabi --prefix=$FREETYPE/install --without-png --without-zlib --enable-shared
sed -i 's/\-version\-info \$(version_info)/-avoid-version/' builds/unix/unix-cc.mk
make
make install

It will compile and install freetype library into $FREETYPE/install. We will link to this install dir later on. It would be possible also to link openjfx font support dynamically against skia library available on Android which already contains freetype. It creates smaller result but can have compatibility problems.

Patching

Download patches javafx-android-compat.patch + android-tools.patch and patch jfx78 repository. I recommend to have look at patches. First one android-compat.patch updates openjfx build script, removes dependency on SharedSecret classes and updates LensLogger to remove dependency on jdk specific PlatformLogger. Second one android-tools.patch creates helper script in android-tools. The script helps to setup javaFX Android projects.

Building

Now is time to try the build. Run following script:

JAVA_HOME=/opt/jdk1.7.0
JDK_HOME=/opt/jdk1.7.0
ANDROID_SDK=/opt/android-sdk-linux
ANDROID_NDK=/opt/android-ndk-r9b
PATH=$JAVA_HOME/bin:$ANDROID_SDK/tools:$ANDROID_SDK/platform-tools:$ANDROID_NDK:$PATH
gradle -PDEBUG -PDALVIK_VM=true -PBINARY_STUB=~/work/binary_stub/linux/rt/lib/ext/jfxrt.jar \
-PFREETYPE_DIR=~/work/freetype/install -PCOMPILE_TARGETS=android

If everything went all right the output is in build/android-sdk :)

UPDATE: If you want to build without jfxrt.jar binary stub comment out jmx and sample apps from settings.gradle.

Create first JavaFX Android project

Use gradle script int android-tools. The script sets the project structure for you.   Following command creates Android HelloWorld project which links to a freshly built javafx runtime and to a HelloWorld application.

  • NAME is a name of Android project.
  • DIR where to create our first project.
  • PACKAGE is package name required by Android. It has nothing to do with a packaging of javafx application.
  • JFX_SDK points to our recently built runtime.
  • JFX_APP points to dist directory of javafx application. (where all application jars sit)
  • JFX_MAIN is fully qualified name of a main class.
gradle -PDEBUG -PDIR=/home/user/work -PNAME=HelloWorld -PPACKAGE=com.helloworld \
-PJFX_SDK=/home/user/work/jfx78/build/android-sdk -PJFX_APP=/home/user/NetBeansProjects/HelloWorld/dist \
-PJFX_MAIN=com.helloworld.HelloWorld createProject

Now cd to the created project and use it like any other android project. ant clean, debug, uninstall, installd will work. I haven't tried it from any IDE Eclipse nor Netbeans.

Special thanks to Stefan Fuchs and Daniel Zwolenski for the repositories used in this blog post.
Comments:

Can we hope soon Oracle will support iOS and Android officially?
It'll be awesome have this support into Netbeans IDE

Posted by Gian Angelo Geminiani on November 15, 2013 at 02:31 PM CET #

Hi,

Thanks for the tutorial !

Currently I managed to build a linux Target version but not an android one :/. I use the following properties :
ANDROID_SDK: /home/told/openjfx/adt-bundle-linux-x86-20131030/sdk
ANDROID_SDK_TARGET: android-19
ANDROID_NDK: /home/told/openjfx/android-ndk-r9b
ANDROID_NDK_TARGET: android-19
FREETYPE_DIR: /home/told/openjfx/freetype-2.5.0.1/install
Compile native text: true
Android Compiler: /home/told/openjfx/android-ndk-r9b/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86/bin/arm-linux-androideabi-gcc
Android Linker: /home/told/openjfx/android-ndk-r9b/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86/bin/arm-linux-androideabi-g++
OS_NAME: linux
OS_ARCH: i386
JAVA_HOME: /home/told/openjfx/jdk1.7.0_45
JDK_HOME: /home/told/openjfx/jdk1.7.0_45
java.runtime.version: 1.7.0_45-b18
java version: 1.7.0
java build number: 18
jdk.runtime.version: 1.7.0_45-b18
jdk version: 1.7.0
jdk build number: 18
minimum java build number: 0
COMPILE_TARGETS: android
COMPILE_FLAGS_FILES: buildSrc/android.gradle
BINARY_STUB: /home/told/openjfx/jdk1.8.0/jre/lib/ext/jfxrt.jar
HUDSON_JOB_NAME: not_hudson
HUDSON_BUILD_NUMBER: 0000
PROMOTED_BUILD_NUMBER: 00
PRODUCT_NAME: OpenJFX
RAW_VERSION: 7.8.0
RELEASE_NAME: 7.8
RELEASE_MILESTONE: ea
UPDATE_STUB_CACHE: false
The CompileOptions.useAnt property has been deprecated and is scheduled to be removed in Gradle 2.0. There is no replacement for this property.
OpenJFX build. Skipping webkit build on Android.

Error comes during the Freetype linking, I have done the Toolchain and Freetype compilation succesfuly and JFX78 patchs were done but I get the following error :
/home/told/openjfx/android-ndk-r9b/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86/bin/../lib/gcc/arm-linux-androideabi/4.8/../../../../arm-linux-androideabi/bin/ld: error: /home/told/openjfx/freetype-2.5.0.1/install/lib/libfreetype.a(ftinit.o): incompatible target

Any idears ?
Best regards,

Posted by Told on November 20, 2013 at 11:42 AM CET #

Haven't you forgotten --host=arm-linux-androideabi when compiling freetype?

Posted by Tomas on November 20, 2013 at 01:31 PM CET #

Many Thanks, jfx78-android-sdk build :)

Your right there was a trouble in the gcc use.

With your advice I noticed that the arm compiler was not used :
checking build system type... x86_64-unknown-linux-gnu
checking host system type... arm-unknown-linux-androideabi
checking for arm-linux-androideabi-gcc... no

--host=arm-linux-androideabi was set but the TOOCHAIN path was invalid... Thanks to your advice I found my mistake...

One last question if you hav time :
- Could you explain the gradle createProject more ?

I created a build.gradle :
buildscript {
repositories {
mavenCentral()
}

dependencies {
classpath 'com.android.tools.build:gradle:0.6.3'
}
}

apply plugin: 'android'

android {
compileSdkVersion 19
}

And then tryed your create project via your gradle createProject command... Get createProject task is unknown...

Thanks again ! :)
Best regards

Posted by Told on November 20, 2013 at 03:26 PM CET #

A gradle script in android-tools is a helper script to setup an android project with references to jfx sdk and jfx application.
When project is setup there are some ant scripts created namely custom-rules.xml and build-extras.xml. Script custom-rules.xml provides copying of jfxrt.jar and native libraries from jfx sdk and also jfx application jar files to right places (e.q. libs) into android project. Script build-extras just wraps one macro from android buildsystem in order to pass extra parameter --core-library for including classes in restricted packages. base.properties contains paths to jfx sdk and jfx application dist dir.
If you look into the build.gradle you will see the sources of all scripts and properties files which are simply dumped into the project during its creation.

I'm just guessing what you're trying to do. I suppose you're trying to build android project using gradle script. The project as it is generated now supports only ant build not gradle.

-Tomas

Posted by Tomas on November 20, 2013 at 04:47 PM CET #

I was trying to build a Android Project with this how2. The build/android-sdk is set up like described. Now when I try to create a project, I always get the same error telling me following...

Execution failed for task ':androidCreateProject'.
> A problem occurred starting process 'command 'android''
...

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':androidCreateProject'.
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:69)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:46)
at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:35)
at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:64)
at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:42)
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:53)
at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
at org.gradle.api.internal.AbstractTask.executeWithoutThrowingTaskFailure(AbstractTask.java:286)
at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.executeTask(AbstractTaskPlanExecutor.java:79)
at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:63)
at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:51)
at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$1.run(DefaultTaskPlanExecutor.java:33)
at org.gradle.internal.Factories$1.create(Factories.java:22)
at org.gradle.cache.internal.DefaultCacheAccess.longRunningOperation(DefaultCacheAccess.java:198)
at org.gradle.cache.internal.DefaultCacheAccess.longRunningOperation(DefaultCacheAccess.java:266)
at org.gradle.cache.internal.DefaultPersistentDirectoryStore.longRunningOperation(DefaultPersistentDirectoryStore.java:135)
at org.gradle.api.internal.changedetection.state.DefaultTaskArtifactStateCacheAccess.longRunningOperation(DefaultTaskArtifactStateCacheAccess.java:93)
at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor.process(DefaultTaskPlanExecutor.java:31)
at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:86)
at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:29)
at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:61)
at org.gradle.execution.DefaultBuildExecuter.access$200(DefaultBuildExecuter.java:23)
at org.gradle.execution.DefaultBuildExecuter$2.proceed(DefaultBuildExecuter.java:67)
at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:61)
at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:54)
at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:166)
at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:113)
at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:81)
at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.run(InProcessBuildActionExecuter.java:64)
at org.gradle.launcher.cli.ExecuteBuildAction.run(ExecuteBuildAction.java:33)
at org.gradle.launcher.cli.ExecuteBuildAction.run(ExecuteBuildAction.java:24)
at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:35)
at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:26)
at org.gradle.launcher.cli.RunBuildAction.run(RunBuildAction.java:50)
at org.gradle.api.internal.Actions$RunnableActionAdapter.execute(Actions.java:171)
at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:201)
at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:174)
at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:170)
at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:139)
at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:33)
at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:22)
at org.gradle.launcher.Main.doAction(Main.java:46)
at org.gradle.launcher.bootstrap.EntryPoint.run(EntryPoint.java:45)
at org.gradle.launcher.Main.main(Main.java:37)
at org.gradle.launcher.bootstrap.ProcessBootstrap.runNoExit(ProcessBootstrap.java:50)
at org.gradle.launcher.bootstrap.ProcessBootstrap.run(ProcessBootstrap.java:32)
at org.gradle.launcher.GradleMain.main(GradleMain.java:23)
Caused by: org.gradle.process.internal.ExecException: A problem occurred starting process 'command 'android''
at org.gradle.process.internal.DefaultExecHandle.setEndStateInfo(DefaultExecHandle.java:192)
at org.gradle.process.internal.DefaultExecHandle.failed(DefaultExecHandle.java:321)
at org.gradle.process.internal.ExecHandleRunner.run(ExecHandleRunner.java:88)
at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:66)
Caused by: java.io.IOException: Cannot run program "android" (in directory "/home/myuser/work"): error=2, No such file or directory
at org.gradle.process.internal.ExecHandleRunner.run(ExecHandleRunner.java:69)
... 1 more
Caused by: java.io.IOException: error=2, No such file or directory
... 2 more

May be you a hint what could be wrong

- Mitchel

Posted by guest on November 21, 2013 at 04:50 PM CET #

Probably android command is not in path.
Adding sdk to the path should help:

export ANDROID_SDK=/opt/android-sdk-linux
export ANDROID_NDK=/opt/android-ndk-r9b
export JAVA_HOME=/opt/jdk1.7.0
export PATH=$JAVA_HOME/bin:$ANDROID_SDK/tools:$ANDROID_SDK/platform-tools:$ANDROID_NDK:$PATH

-Tomas

Posted by Tomas on November 21, 2013 at 05:25 PM CET #

Hi Thomas

The SDK is already in the path...

mitch@ubuntu:~$ echo $ANDROID_SDK
/opt/android-sdk-linux
mitch@ubuntu:~$ echo $ANDROID_NDK
/opt/android-ndk-r9b
mitch@ubuntu:~$ echo $JAVA_HOME
/opt/jdk1.7.0
mitch@ubuntu:~$ echo $PATH
/opt/jdk1.7.0/bin:/opt/android-sdk-linux/tools:/opt/android-sdk-linux/platform-tools:/opt/android-ndk-r9b:/opt/jdk1.7.0/bin:/opt/android-sdk-linux/tools:/opt/android-sdk-linux/platform-tools:/opt/android-ndk-r9b:/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

I can start 'android' from the terminal, that works good. But when I try to start following in the terminal it doesn't work...

mitch@ubuntu:~/Downloads/jfx78$ sudo /home/mitch/Downloads/gradle-1.9/bin/gradle -PDEBUG -PDIR=/opt/android-sdk-linux/tools/ -PNAME=HelloWorld -PPACKAGE=com.helloworld -PJFX_SDK=/home/apay/Downloads/jfx78/build/android-sdk -PJFX_APP=/home/apay/projects/android/HelloWorld/dist -PJFX_MAIN=com.helloworld.HelloWorld createProject -PANDROID_SDK=/opt/android-sdk-linux --stacktrace

- Mitchel

Posted by Mitchel on November 22, 2013 at 09:37 AM CET #

-PDIR=<existing directory where to create the project>
I suppose that it should not be /opt/android-sdk-linux/tools/

If that doesn't help you can try to use full path to android executable.
In android-tools/bild.gradle look for task androidCreateProject and rewrite

executable "$conf.ext.sdk/tools/android"

hope this helps
-Tomas

Posted by Tomas on November 22, 2013 at 01:25 PM CET #

Thank you sooo much :o)
Worked out now. Problem was the full path to the android executable.

- Mitchel

Posted by Mitchel on November 22, 2013 at 02:33 PM CET #

There is not a full sample,why?

Posted by damo on November 25, 2013 at 03:31 AM CET #

Hello,

I am integrating javafx apps in android with the help of this blog. But, where to find the patch mention in this blog.

I am not able to find out the following patches

javafx-android-compat.patch + android-tools.patch

Please help me.

Thanks in advance

Posted by Shahid on December 16, 2013 at 06:04 AM CET #

If you read the text carefully both links are there.
But anyway use repository, instructions and javafx binary runtime for android at this address https://bitbucket.org/javafxports/android/wiki/Home

-Tomas

Posted by Tomas on December 16, 2013 at 09:10 AM CET #

Hi,

I am getting the following build issue. can anybody help please.

my sdk path:/opt/android-sdk-linux
ndk path /opt/android-ndk-r9b

Creating properties on demand (a.k.a. dynamic properties) has been deprecated and is scheduled to be removed in Gradle 2.0. Please read http://gradle.org/docs/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html for information on the replacement for dynamic properties.
Deprecated dynamic property: "ANDROID" on "root project 'jfx78'", value: "{}".
Can't build Android. Path to SDK or NDK was not set or is invalid!

FAILURE: Build failed with an exception.

* Where:
Build file '/opt/jfx78/build.gradle' line: 618

* What went wrong:
A problem occurred evaluating root project 'jfx78'.
> Unable to determine compilation platform, must specify valid COMPILE_TARGETS!

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Posted by Shahid on December 18, 2013 at 07:34 AM CET #

Hi,

I am getting the following error

FAILURE: Build failed with an exception.

* Where:
Build file '/opt/jfx78/build.gradle' line: 618

* What went wrong:
A problem occurred evaluating root project 'jfx78'.
> Unable to determine compilation platform, must specify valid COMPILE_TARGETS!

when running the following command

gradle -PDEBUG -PDALVIK_VM=true -PBINARY_STUB=/opt/jdk1.8.0/jre/lib/ext/jfxrt.jar \
-PFREETYPE_DIR=/opt/freetype/install -PCOMPILE_TARGETS=android \ -PANDROID_SDK=/opt/android-sdk-linux -PANDROID_NDK=/opt/android-ndk-r9b \ -PCOMPILE_GSTREAMER=false

Please help.

Posted by Shahid on December 27, 2013 at 06:09 AM CET #

These instructions are a bit outdated now. Please read the following blog post https://blogs.oracle.com/jfxprg/entry/javafx_on_android and use latest javafx android port.
-Tomas

Posted by Tomas on December 27, 2013 at 10:13 AM CET #

Post a Comment:
  • HTML Syntax: NOT allowed
About

JavaFX is a Java GUI toolkit, partially developed from Prague, Czech Republic. The Prague team uses this blog to post articles, code samples and insights about the range of topics the team members specialize in. This includes JavaFX Scenegraph (javafx.scene.*), JavaFX Core libraries & animations, iOS port & Android port.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today