{"id":45105,"date":"2021-06-18T07:10:41","date_gmt":"2021-06-18T07:10:41","guid":{"rendered":"https:\/\/foojay.io\/?p=45105"},"modified":"2022-02-11T09:12:35","modified_gmt":"2022-02-11T09:12:35","slug":"creating-mobile-apps-with-javafx-part-2","status":"publish","type":"post","link":"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/","title":{"rendered":"How to Create Mobile Apps with JavaFX (Part 2)"},"content":{"rendered":"\n    <div class=\"article__table\">\n        <div class=\"article__table-header\">\n            <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                <path d=\"M8 6H21\" stroke=\"#3562E5\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" \/>\n                <path d=\"M8 12H21\" stroke=\"#3562E5\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" \/>\n                <path d=\"M8 18H21\" stroke=\"#3562E5\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" \/>\n                <path d=\"M3 6H3.01\" stroke=\"#3562E5\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" \/>\n                <path d=\"M3 12H3.01\" stroke=\"#3562E5\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" \/>\n                <path d=\"M3 18H3.01\" stroke=\"#3562E5\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" \/>\n            <\/svg>\n            Table of Contents\n            <svg class=\"chevron\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                <path d=\"M18 15L12 9L6 15\" stroke=\"#3562E5\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/>\n            <\/svg>\n        <\/div>\n        <div class=\"article__table-body\"><ul><li><a href=\"#h3-0--luon-plugin-for-aven\">GluonFX plugin for Maven<\/a><\/li><li><a href=\"#h3-1--raal-\">GraalVM<\/a><\/li><li><a href=\"#h3-2--luon-ubstrate\">Gluon Substrate<\/a><\/li><li><a href=\"#h3-3--esktop\">JVM Desktop<\/a><\/li><li><a href=\"#h3-4-i-\">iOS<\/a><\/li><li><a href=\"#h3-5--onfiguration\">IOS Configuration<\/a><\/li><li><a href=\"#h3-6--ndroid\">Android<\/a><\/li><li><a href=\"#h3-7--ndroid-onfiguration\">Android Configuration<\/a><\/li><li><a href=\"#h3-8--ext\">Next<\/a><\/li><\/ul><\/div><\/div><!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD HTML 4.0 Transitional\/\/EN\" \"http:\/\/www.w3.org\/TR\/REC-html40\/loose.dtd\">\n<?xml encoding=\"utf-8\" ?><html><body><p>In <a href=\"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx---part-1\/\">Part 1<\/a>, we introduced a mobile app game, TiltMaze, written completely in JavaFX, which you can download from either the <a href=\"https:\/\/apps.apple.com\/app\/id1566464439\" target=\"_blank\" rel=\"noreferrer noopener\">Apple App Store<\/a> or <a href=\"https:\/\/play.google.com\/store\/apps\/details?id=com.asgteach.accelerometer.TiltMaze\" target=\"_blank\" rel=\"noreferrer noopener\">Google Play<\/a> and install it on your mobile device or tablet. <\/p>\n\n\n\n<p>In this article, we'll discuss the technologies we use with JavaFX to build the JVM byte code version as well as native images that target iOS and Android devices.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h3-0--luon-plugin-for-aven\">GluonFX plugin for Maven<\/h3>\n\n\n\n<p>Gluon (<a target=\"_blank\" href=\"https:\/\/docs.gluonhq.com\/#_the_gluon_client_plugin_for_maven\">https:\/\/docs.gluonhq.com\/<\/a>) has a Maven plugin that lets you build native-image JavaFX applications targeting multiple platforms. While Java has always been about compiling Java byte code that runs in a Java Virtual Machine (JVM) anywhere and everywhere (Write Once, Run Anywhere), there are prohibitions with running byte code in the mobile environment. Besides the performance penalties of executing byte code in a virtual machine and the relatively long startup time, Apple prohibits JVM applications on mobile targets and Android Java is non-standard. If you choose to leave the JavaFX world for native toolkits, you need to acquire expertise in Objective C\/Swift as well as the respective native UI toolkits to target these platforms. Spoiler alert: I didn&rsquo;t do that!<\/p>\n\n\n\n<p>Enter Gluon, JavaFX, and GraalVM. The GluonFX plugin leverages GraalVM, OpenJDK and OpenJFX (JavaFX) by compiling the Java client application and all its required dependencies into native code. The target platform directly executes the native application.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>While putting together this series, Gluon has been busy updating their Gluon \"Client\" maven plugin. As of this week, the plugin is now known as the \"GluonFX\" plugin at the 1.0.0 version. We have updated this post, but, as always, refer to the docs at <a href=\"https:\/\/t.co\/O1E01TiQkY?amp=1\" rel=\"noreferrer noopener\" target=\"_blank\">https:\/\/docs.gluonhq.com\/#_gluonfx_plugin_for_maven<\/a>. This is a major milestone for building native applications from Java and <a target=\"_blank\" href=\"https:\/\/twitter.com\/hashtag\/JavaFX?src=hashtag_click\">#JavaFX<\/a> client apps for desktop, mobile and embedded.<\/p><\/blockquote>\n\n\n\n<figure class=\"wp-block-embed is-type-rich is-provider-twitter wp-block-embed-twitter\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"twitter-tweet\" data-width=\"500\" data-dnt=\"true\"><p lang=\"en\" dir=\"ltr\">The Gluon \"Client\" maven plugin is now known as the \"GluonFX\" plugin and we are at the 1.0.0 version. <br>Docs at <a target=\"_blank\" href=\"https:\/\/t.co\/O1E01TiQkY\">https:\/\/t.co\/O1E01TiQkY<\/a> <br>A major milestone for building native applications from Java and <a target=\"_blank\" href=\"https:\/\/twitter.com\/hashtag\/JavaFX?src=hash&amp;ref_src=twsrc%5Etfw\">#JavaFX<\/a> client apps for desktop, mobile and embedded.<\/p>&mdash; GluonHQ (@GluonHQ) <a target=\"_blank\" href=\"https:\/\/twitter.com\/GluonHQ\/status\/1404353863055032322?ref_src=twsrc%5Etfw\">June 14, 2021<\/a><\/blockquote><script async src=\"https:\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"><\/script>\n<\/div><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h3-1--raal-\"><strong>GraalVM<\/strong><\/h3>\n\n\n\n<p>What is GraalVM and why should I use it? GraalVM is a high-performance JDK distribution from Oracle designed to accelerate the execution of applications written in Java (and other JVM languages). GraalVM allows compiling Java applications ahead-of-time (AOT) into native executables for faster startup time and lower memory overhead. Gluon leverages the GraalVM Community Edition to build your mobile application to the target platform using AOT compilation and linking with native libraries. The package step builds the executable for the target device.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h3-2--luon-ubstrate\"><strong>Gluon Substrate<\/strong><\/h3>\n\n\n\n<p>If your head is spinning, wondering how to collect all the native libraries, signing requirements, and packaging details into one native executable, fear not! Gluon Substrate (with its GluonFX Maven plugin) pulls together the pieces and dependencies for you. Gluon Substrate lets you build a specific target package that includes all the necessary dependencies.<\/p>\n\n\n\n<p>Here are the steps we took to build TiltMaze.&nbsp;<\/p>\n\n\n\n<p>We built our Maven-based JavaFX application from Gluon&rsquo;s starter page: <a target=\"_blank\" href=\"https:\/\/start.gluon.io\/\">https:\/\/start.gluon.io\/<\/a>. This helpful interactive web page lets you specify the main mobile components for your application. Options include Glisten (Gluon&rsquo;s mobile-friendly licensed-based skinned controls), Glisten Afterburner (light-weight dependency injection), Gluon Maps (integrate map functionality), Connect (data resources), CloudLink (connect your mobile application to the enterprise), and Attach (use low-level features of the mobile device in a device and OS-agnostic way). Some of these options require a license from Gluon, but Gluon offers a generous non-pay license for open source and student projects. This page also includes a helpful list of features you can leverage with your mobile application all summarized in one place.<\/p>\n\n\n\n<p>We selected Glisten (to use the mobile-friendly skinned controls), Glisten Afterburner, FXML, and Attach and downloaded the resulting Maven project to our local work machine.<\/p>\n\n\n\n<p>Note: Install maven here: <a target=\"_blank\" href=\"https:\/\/maven.apache.org\/install.html\">https:\/\/maven.apache.org\/install.html<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h3-3--esktop\"><strong>JVM Desktop<\/strong><\/h3>\n\n\n\n<p>Although I use an IDE to edit my project, a command line interface performs all the builds to run and install the application. The first task is to build and run the application with regular byte code on the JVM for application design and debugging. My default host machine is Mac OSX with the standard JVM using the default installed (11+) Java and JavaFX. This is the environment we initially used to build and test TiltMaze; the build\/run cycle is very fast and efficient for debugging. (For accelerometer testing, we built a small test program to react to the device readings and ran this test on the phone.)<\/p>\n\n\n\n<p>Here is the maven command you run from the top-level directory of the TiltMaze project directory:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ mvn javafx:run<\/pre>\n\n\n\n<p>At some point, though, you must test your application on the target hardware. Because native builds take much longer than Java byte code builds, you should perform as much coding, debugging, and testing as possible before you move to actual device testing.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h3-4-i-\"><strong>iOS<\/strong><\/h3>\n\n\n\n<p>Gluon provides documentation on what you need to build native iOS images. If your goal is to create an app that others can install from the App Store, you&rsquo;ll need a Mac, Xcode (Apple&rsquo;s development tool, free to install), an Apple Developer License ($99\/year), and Apple signing certificates (one for installing on your own device for testing and a different one for uploading the application to the Apple Connect site).<\/p>\n\n\n\n<p><strong>Updates on building native images.<\/strong><\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p><a target=\"_blank\" href=\"https:\/\/gluonhq.com\/use-github-actions-to-automate-your-gluon-build-and-release-cycle\/\">Gluon has leveraged GitHub Actions<\/a> that allow you to build native images in a GitHub-hosted cloud environment using the required host operating system. This works for both iOS and Android targets, where the required hosts (Mac OSX and Linux, respectively) can be configured remotely through GitHub Actions.&nbsp; We didn't go this route, but GitHub Actions provide a great alternative to building everything locally.<\/p><\/blockquote>\n\n\n\n<p>Before testing your app, it&rsquo;s best to build a basic do-nothing Xcode project and install it on your device. This verifies that you have Xcode working with a valid apple ID and your signing certificate lets you install an app to your device.<\/p>\n\n\n\n<p>Next, follow the documentation for the GluonFX plugin. This&nbsp; plugin manages all the pieces for creating native images based on GraalVM JDK and Gluon Substrate from your Java\/JavaFX source code.&nbsp; Download and unpack GraalVM from the latest Gluon community edition, <a href=\"https:\/\/github.com\/gluonhq\/graal\/releases\/latest\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/gluonhq\/graal\/releases\/latest<\/a>. For Mac and iOS, download <a href=\"https:\/\/github.com\/gluonhq\/graal\/releases\/download\/21.1.0-dev-20210415_0700\/graalvm-svm-darwin-gluon-21.1.0-dev.zip\" target=\"_blank\" rel=\"noreferrer noopener\">graalvm-svm-darwin-gluon-21.1.0-dev.zip<\/a> (or the most current release version).<\/p>\n\n\n\n<p>Next, set environment variable <code>GRAALVM_HOME<\/code> to the GraalVM JDK:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ export GRAALVM_HOME=\/path\/to\/graalvm-svm-darwin-gluon-21.1.0-dev\/Contents\/Home<\/pre>\n\n\n\n<p>and set <code>JAVA_HOME<\/code> as well:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ export JAVA_HOME=$GRAALVM_HOME<\/pre>\n\n\n\n<p>For IOS targets, use<a target=\"_blank\" href=\"https:\/\/brew.sh\"> Homebrew<\/a> to install the following packages:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ brew install --HEAD libusbmuxd\n$ brew install --HEAD libimobiledevice<\/pre>\n\n\n\n<p>The GluonFX plugin lets you define profiles, so you can specify the desired target in your build\/run\/install commands. When you build your project from Gluon&rsquo;s starter page (<a target=\"_blank\" href=\"https:\/\/start.gluon.io\/\">https:\/\/start.gluon.io\/<\/a>), profiles for ios and android (as well as desktop) are predefined in the <code>pom.xml<\/code> file.<\/p>\n\n\n\n<p>Here is the sequence we use to build and install\/run TiltMaze on a connected iPhone. As before, we are in the top-level directory of the TiltMaze project.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ mvn -Pios gluonfx:build<\/pre>\n\n\n\n<p>The build action performs a compile followed by a link. Next, connect your iPhone via USB to your Mac and run the following command.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ mvn -Pios gluonfx:nativerun<\/pre>\n\n\n\n<p>This command both installs the application created by the build action and runs the application. At this point, standard output from <code>System.out.println<\/code> statements appear on the desktop console and any logging is written to log files in the appropriate target subdirectory. The application is installed on your phone, so you can also exit and re-run the application from the phone. Exiting the application, however, disconnects remote execution from the Mac console.<\/p>\n\n\n\n<p>Note: If you're having issues getting a successful build or install, try<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Turn on verbose mode in the gluonfx plugin.<\/li><\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"xml\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">                &lt;artifactId&gt;gluonfx-maven-plugin&lt;\/artifactId&gt;\n                &lt;version&gt;${gluonfx.maven.plugin.version}&lt;\/version&gt;\n                &lt;configuration&gt;\n                    . . .\n                    &lt;verbose&gt;true&lt;\/verbose&gt;\n                &lt;\/configuration&gt;<\/pre>\n\n\n\n<ul class=\"wp-block-list\"><li>Return to the <a href=\"https:\/\/docs.gluonhq.com\/#platforms_ios\" target=\"_blank\" rel=\"noreferrer noopener\">Gluon Documents<\/a> for the iOS platform to make sure your pom.xml file is configured correctly.<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h3-5--onfiguration\"><strong>IOS Configuration<\/strong><\/h3>\n\n\n\n<p>The GluonFX plugin builds all its output in the <code>target<\/code> subdirectory. In the process, it generates default configurations for ios-specific settings, such as file <strong>Default-Info.plist<\/strong>, and a complete icon set under subdirectory <code>target\/gluonfx\/arm64-ios\/gensrc\/ios\/<\/code>. File <strong>Default-Info.plist<\/strong> is an XML-based file where you specify ios-specific features, such as support for portrait or landscape modes, or if your app uses the accelerometer, for example.&nbsp;<\/p>\n\n\n\n<p>The GluonFX plugin generates a default icon set for your application based on the Gluon icon. Since these default settings and icons are generated anew for each build, any modifications you make will not be retained. Therefore, you should copy any customized files from <code>target\/gluonfx\/arm64-ios\/gensrc\/ios\/<\/code> (your icon files and your <strong>Default-Info.plist<\/strong> file) to <code>src\/ios\/<\/code>. (Note: we have already customized <strong>Default-Info.plist<\/strong> and the icon set and moved these files to <code>src\/ios\/<\/code>.)&nbsp;<\/p>\n\n\n\n<p>In TiltMaze, we modified <strong>Default-Info.plist<\/strong> to permit portrait mode only, use the accelerometer, and not use Bluetooth.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"xml\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;dict&gt;\n          . . .\n        &lt;key&gt;CFBundleName&lt;\/key&gt;\n        &lt;string&gt;TiltMaze&lt;\/string&gt;\n         . . .\n        &lt;key&gt;UISupportedInterfaceOrientations&lt;\/key&gt;\n        &lt;array&gt;\n            &lt;string&gt;UIInterfaceOrientationPortrait&lt;\/string&gt;\n        &lt;\/array&gt;\n        &lt;key&gt;UISupportedInterfaceOrientations~ipad&lt;\/key&gt;\n        &lt;array&gt;\n            &lt;string&gt;UIInterfaceOrientationPortrait&lt;\/string&gt;\n        &lt;\/array&gt;\n        &lt;key&gt;UIRequiredDeviceCapabilities&lt;\/key&gt;\n        &lt;array&gt;\n            &lt;string&gt;arm64&lt;\/string&gt;\n            &lt;string&gt;accelerometer&lt;\/string&gt;\n        &lt;\/array&gt;\n          . . .\n        &lt;key&gt;NSBluetoothAlwaysUsageDescription&lt;\/key&gt;\n        &lt;string&gt;This app does not use Bluetooth&lt;\/string&gt;        \n        &lt;key&gt;NSMotionUsageDescription&lt;\/key&gt;\n        &lt;string&gt;This app uses accelerometer services&lt;\/string&gt;\n&lt;\/dict&gt;\n<\/pre>\n\n\n\n<p>We created our own application icon, which you supply in various sizes for the App Store and the device icons. The entire icon set includes 18 different PNG images.<\/p>\n\n\n\n<p>Here are several resources to generate the icons for the various devices.&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>App Icon generator:&nbsp; <a target=\"_blank\" href=\"https:\/\/appicon.co\/\">https:\/\/appicon.co\/<\/a> -- generates a set of icons from a single 1024 x 1024 PNG file.<\/li><li>Maven Image Builder: You add a scale goal and specify your desired sizes, an input image and an output image name for each size. See the readme file here for more information: <a target=\"_blank\" href=\"https:\/\/github.com\/zayass\/image-maven-plugin\">https:\/\/github.com\/zayass\/image-maven-plugin<\/a><\/li><\/ul>\n\n\n\n<p>Here is the TiltMaze icon. Be sure to leave generous margins around the icon, since Apple will round the corners, possibly cutting into your design.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"248\" height=\"248\" src=\"https:\/\/foojay.io\/wp-content\/uploads\/2021\/06\/tiltmaze-logo-small.png\" alt=\"\" class=\"wp-image-45106\" srcset=\"https:\/\/foojay.io\/wp-content\/uploads\/2021\/06\/tiltmaze-logo-small.png 248w, https:\/\/foojay.io\/wp-content\/uploads\/2021\/06\/tiltmaze-logo-small-80x80.png 80w, https:\/\/foojay.io\/wp-content\/uploads\/2021\/06\/tiltmaze-logo-small-96x96.png 96w\" sizes=\"auto, (max-width: 248px) 100vw, 248px\" \/><figcaption>TiltMaze icon<\/figcaption><\/figure><\/div>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h3-6--ndroid\"><strong>Android<\/strong><\/h3>\n\n\n\n<p>For Android applications, your development environment is Linux. If you have a Linux box, great! You&rsquo;re ready to install GraalVM, Maven, and optionally your favorite IDE, and start building Android applications. Faced with the prospect of buying extra hardware, I decided to use Oracle&rsquo;s Virtual Box and install Ubuntu Linux on my Mac. This is a very workable solution, but there are a few cautions:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>The &ldquo;normal&rdquo; install allocates 16GB for the Ubuntu virtual image, which is too small. After installation, I used the <code>gparted<\/code> utility to increase the size up to 50GB. It's much easier if you configure a larger allocation during setup.<\/li><li>Normal memory allocation is 4GB; again, too small. I increased memory to more than 10GB.<\/li><li>Close as many running programs as possible on your Mac before running native builds for Android. This reduces the elapsed time needed to complete the task.<\/li><li>Configure file sharing so that you can work with the same physical files with both Linux and Mac OSX.<\/li><\/ul>\n\n\n\n<p>To target an application for the Android platform, you need the Android SDK. Fortunately, the GluonFX plugin automatically downloads and configures the required Android SDK packages for you! (Alternatively, with GitHub Actions you can build your android application in a cloud-hosted Linux system.)<\/p>\n\n\n\n<p>Here&rsquo;s a summary of the steps we followed to create the Android native image after configuring Ubuntu Linux with the larger disk image, more memory, and file sharing.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Download and install the latest Gluon community edition for Linux, <a href=\"https:\/\/github.com\/gluonhq\/graal\/releases\/latest\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/gluonhq\/graal\/releases\/latest<\/a>. Currently, this is <kbd>graalvm-svm-linux-gluon-21.1.0-dev.zip<\/kbd>. Set the <code>GRAALVM_HOME<\/code> environment variable to the GraalVM install directory on your Linux system.<\/li><li>Make sure you have <code>gcc<\/code> version 6 or higher.<\/li><li>Make sure you have <code>ld<\/code> version 2.26 or higher.<\/li><li>You will use profile target <code>android<\/code> in your Maven <code>pom.xml<\/code> file.<\/li><\/ul>\n\n\n\n<p>Build the project for Android with&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ mvn -Pandroid gluonfx:build<\/pre>\n\n\n\n<p>This runs the compilation phase and links the compiled objects into an Android executable.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Generate an Android application package (an APK):<\/li><\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ mvn -Pandroid gluonfx:package<\/pre>\n\n\n\n<ul class=\"wp-block-list\"><li>Install the application on a connected Android device. Note that \"USB debugging\" must be enabled on the connected device. To enable USB debugging follow the steps listed <a href=\"https:\/\/developer.android.com\/training\/basics\/firstapp\/running-app#RealDevice\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a>. Install with the following action.<\/li><\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ mvn -Pandroid gluonfx:install<\/pre>\n\n\n\n<ul class=\"wp-block-list\"><li>And finally, launch the application on the connected device with<\/li><\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ mvn -Pandroid gluonfx:nativerun<\/pre>\n\n\n\n<p>The same as with the iOS target, you can configure <code>&lt;verbose&gt;true&lt;\/verbose&gt;<\/code> mode for help in identifying configuration errors. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h3-7--ndroid-onfiguration\"><strong>Android Configuration<\/strong><\/h3>\n\n\n\n<p>Similar to the iOS targets, the Gluon Plugin generates default settings and application resources for the Android target in subdirectory <code>target\/gluonfx\/aarch64-android\/gensrc\/android\/res<\/code>. Copy the configuration <strong>AndroidManifest.xml <\/strong>and icon PNG files to <code>src\/android\/res<\/code> before customizing these files.&nbsp;<\/p>\n\n\n\n<p>In our case, we specify the application runs only in portrait mode and requires the accelerometer:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"xml\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;uses-feature android:name=\"android.hardware.sensor.accelerometer\" android:required=\"true\" \/&gt;<\/pre>\n\n\n\n<p>We provide Android-specific icon resource files for TiltMaze under <code>src\/android\/res\/<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h3-8--ext\">Next<\/h3>\n\n\n\n<p>In <a href=\"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-3\/\">Part 3<\/a>, we'll show you how to upload a mobile application to the respective mobile app stores.<\/p>\n<\/body><\/html>\n","protected":false},"excerpt":{"rendered":"<p>In  Part 1, we introduced a mobile app game, TiltMaze, written completely in JavaFX, which you can download from either the Apple App Store or Google Play and install it on your mobile device or tablet. <\/p>\n<p>In this article, we&#8217;ll discuss the technologies we use with JavaFX to build the JVM byte code version as well as native images that target iOS and Android devices.<\/p>\n","protected":false},"author":185,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[381,193,227],"tags":[],"class_list":["post-45105","post","type-post","status-publish","format-standard","hentry","category-game","category-javafx","category-maven"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Creating Mobile Apps with JavaFX - Part 2 | foojay<\/title>\n<meta name=\"description\" content=\"Let&#039;s discuss technologies we use with JavaFX to build JVM byte code versions as well as native images that target iOS and Android devices!\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Creating Mobile Apps with JavaFX - Part 2 | foojay\" \/>\n<meta property=\"og:description\" content=\"Let&#039;s discuss technologies we use with JavaFX to build JVM byte code versions as well as native images that target iOS and Android devices!\" \/>\n<meta property=\"og:url\" content=\"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/\" \/>\n<meta property=\"og:site_name\" content=\"foojay\" \/>\n<meta property=\"article:published_time\" content=\"2021-06-18T07:10:41+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-02-11T09:12:35+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/foojay.io\/wp-content\/uploads\/2021\/06\/tiltmaze-logo-small.png\" \/>\n<meta name=\"author\" content=\"Gail Anderson\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Gail Anderson\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/foojay.io\\\/today\\\/creating-mobile-apps-with-javafx-part-2\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/foojay.io\\\/today\\\/creating-mobile-apps-with-javafx-part-2\\\/\"},\"author\":{\"name\":\"Gail Anderson\",\"@id\":\"https:\\\/\\\/foojay.io\\\/#\\\/schema\\\/person\\\/8591e0904fb52e209e4e054fef14f62f\"},\"headline\":\"How to Create Mobile Apps with JavaFX (Part 2)\",\"datePublished\":\"2021-06-18T07:10:41+00:00\",\"dateModified\":\"2022-02-11T09:12:35+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/foojay.io\\\/today\\\/creating-mobile-apps-with-javafx-part-2\\\/\"},\"wordCount\":2010,\"commentCount\":8,\"publisher\":{\"@id\":\"https:\\\/\\\/foojay.io\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/foojay.io\\\/today\\\/creating-mobile-apps-with-javafx-part-2\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/foojay.io\\\/wp-content\\\/uploads\\\/2021\\\/06\\\/tiltmaze-logo-small.png\",\"articleSection\":[\"Game Development\",\"JavaFX\",\"Maven\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/foojay.io\\\/today\\\/creating-mobile-apps-with-javafx-part-2\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/foojay.io\\\/today\\\/creating-mobile-apps-with-javafx-part-2\\\/\",\"url\":\"https:\\\/\\\/foojay.io\\\/today\\\/creating-mobile-apps-with-javafx-part-2\\\/\",\"name\":\"Creating Mobile Apps with JavaFX - Part 2 | foojay\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/foojay.io\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/foojay.io\\\/today\\\/creating-mobile-apps-with-javafx-part-2\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/foojay.io\\\/today\\\/creating-mobile-apps-with-javafx-part-2\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/foojay.io\\\/wp-content\\\/uploads\\\/2021\\\/06\\\/tiltmaze-logo-small.png\",\"datePublished\":\"2021-06-18T07:10:41+00:00\",\"dateModified\":\"2022-02-11T09:12:35+00:00\",\"description\":\"Let's discuss technologies we use with JavaFX to build JVM byte code versions as well as native images that target iOS and Android devices!\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/foojay.io\\\/today\\\/creating-mobile-apps-with-javafx-part-2\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/foojay.io\\\/today\\\/creating-mobile-apps-with-javafx-part-2\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/foojay.io\\\/today\\\/creating-mobile-apps-with-javafx-part-2\\\/#primaryimage\",\"url\":\"https:\\\/\\\/foojay.io\\\/wp-content\\\/uploads\\\/2021\\\/06\\\/tiltmaze-logo-small.png\",\"contentUrl\":\"https:\\\/\\\/foojay.io\\\/wp-content\\\/uploads\\\/2021\\\/06\\\/tiltmaze-logo-small.png\",\"width\":248,\"height\":248},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/foojay.io\\\/today\\\/creating-mobile-apps-with-javafx-part-2\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/foojay.io\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to Create Mobile Apps with JavaFX (Part 2)\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/foojay.io\\\/#website\",\"url\":\"https:\\\/\\\/foojay.io\\\/\",\"name\":\"foojay\",\"description\":\"a place for friends of OpenJDK\",\"publisher\":{\"@id\":\"https:\\\/\\\/foojay.io\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/foojay.io\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/foojay.io\\\/#organization\",\"name\":\"foojay\",\"url\":\"https:\\\/\\\/foojay.io\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/foojay.io\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/foojay.io\\\/wp-content\\\/uploads\\\/2020\\\/04\\\/cropped-Favicon.png\",\"contentUrl\":\"https:\\\/\\\/foojay.io\\\/wp-content\\\/uploads\\\/2020\\\/04\\\/cropped-Favicon.png\",\"width\":512,\"height\":512,\"caption\":\"foojay\"},\"image\":{\"@id\":\"https:\\\/\\\/foojay.io\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/x.com\\\/foojay2020\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/foojay.io\\\/#\\\/schema\\\/person\\\/8591e0904fb52e209e4e054fef14f62f\",\"name\":\"Gail Anderson\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/40623f418590666d23e1559628072f84d24515e323c2f4a6c09f6814f348e16a?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/40623f418590666d23e1559628072f84d24515e323c2f4a6c09f6814f348e16a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/40623f418590666d23e1559628072f84d24515e323c2f4a6c09f6814f348e16a?s=96&d=mm&r=g\",\"caption\":\"Gail Anderson\"},\"description\":\"Gail is a Java Champion and Oracle Groundbreaker Ambassador. She is Director of Research and founding member of the Anderson Software Group, a leading provider of training courses in Java, JavaFX, Python, Go, Modern C++, and other programming languages.\",\"sameAs\":[\"http:\\\/\\\/asgteach.com\",\"https:\\\/\\\/x.com\\\/gail_asgteach\"],\"url\":\"https:\\\/\\\/foojay.io\\\/today\\\/author\\\/gail-anderson\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Creating Mobile Apps with JavaFX - Part 2 | foojay","description":"Let's discuss technologies we use with JavaFX to build JVM byte code versions as well as native images that target iOS and Android devices!","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/","og_locale":"en_US","og_type":"article","og_title":"Creating Mobile Apps with JavaFX - Part 2 | foojay","og_description":"Let's discuss technologies we use with JavaFX to build JVM byte code versions as well as native images that target iOS and Android devices!","og_url":"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/","og_site_name":"foojay","article_published_time":"2021-06-18T07:10:41+00:00","article_modified_time":"2022-02-11T09:12:35+00:00","og_image":[{"url":"https:\/\/foojay.io\/wp-content\/uploads\/2021\/06\/tiltmaze-logo-small.png","type":"","width":"","height":""}],"author":"Gail Anderson","twitter_misc":{"Written by":"Gail Anderson","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/#article","isPartOf":{"@id":"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/"},"author":{"name":"Gail Anderson","@id":"https:\/\/foojay.io\/#\/schema\/person\/8591e0904fb52e209e4e054fef14f62f"},"headline":"How to Create Mobile Apps with JavaFX (Part 2)","datePublished":"2021-06-18T07:10:41+00:00","dateModified":"2022-02-11T09:12:35+00:00","mainEntityOfPage":{"@id":"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/"},"wordCount":2010,"commentCount":8,"publisher":{"@id":"https:\/\/foojay.io\/#organization"},"image":{"@id":"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/#primaryimage"},"thumbnailUrl":"https:\/\/foojay.io\/wp-content\/uploads\/2021\/06\/tiltmaze-logo-small.png","articleSection":["Game Development","JavaFX","Maven"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/","url":"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/","name":"Creating Mobile Apps with JavaFX - Part 2 | foojay","isPartOf":{"@id":"https:\/\/foojay.io\/#website"},"primaryImageOfPage":{"@id":"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/#primaryimage"},"image":{"@id":"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/#primaryimage"},"thumbnailUrl":"https:\/\/foojay.io\/wp-content\/uploads\/2021\/06\/tiltmaze-logo-small.png","datePublished":"2021-06-18T07:10:41+00:00","dateModified":"2022-02-11T09:12:35+00:00","description":"Let's discuss technologies we use with JavaFX to build JVM byte code versions as well as native images that target iOS and Android devices!","breadcrumb":{"@id":"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/#primaryimage","url":"https:\/\/foojay.io\/wp-content\/uploads\/2021\/06\/tiltmaze-logo-small.png","contentUrl":"https:\/\/foojay.io\/wp-content\/uploads\/2021\/06\/tiltmaze-logo-small.png","width":248,"height":248},{"@type":"BreadcrumbList","@id":"https:\/\/foojay.io\/today\/creating-mobile-apps-with-javafx-part-2\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/foojay.io\/"},{"@type":"ListItem","position":2,"name":"How to Create Mobile Apps with JavaFX (Part 2)"}]},{"@type":"WebSite","@id":"https:\/\/foojay.io\/#website","url":"https:\/\/foojay.io\/","name":"foojay","description":"a place for friends of OpenJDK","publisher":{"@id":"https:\/\/foojay.io\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/foojay.io\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/foojay.io\/#organization","name":"foojay","url":"https:\/\/foojay.io\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/foojay.io\/#\/schema\/logo\/image\/","url":"https:\/\/foojay.io\/wp-content\/uploads\/2020\/04\/cropped-Favicon.png","contentUrl":"https:\/\/foojay.io\/wp-content\/uploads\/2020\/04\/cropped-Favicon.png","width":512,"height":512,"caption":"foojay"},"image":{"@id":"https:\/\/foojay.io\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/foojay2020"]},{"@type":"Person","@id":"https:\/\/foojay.io\/#\/schema\/person\/8591e0904fb52e209e4e054fef14f62f","name":"Gail Anderson","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/40623f418590666d23e1559628072f84d24515e323c2f4a6c09f6814f348e16a?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/40623f418590666d23e1559628072f84d24515e323c2f4a6c09f6814f348e16a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/40623f418590666d23e1559628072f84d24515e323c2f4a6c09f6814f348e16a?s=96&d=mm&r=g","caption":"Gail Anderson"},"description":"Gail is a Java Champion and Oracle Groundbreaker Ambassador. She is Director of Research and founding member of the Anderson Software Group, a leading provider of training courses in Java, JavaFX, Python, Go, Modern C++, and other programming languages.","sameAs":["http:\/\/asgteach.com","https:\/\/x.com\/gail_asgteach"],"url":"https:\/\/foojay.io\/today\/author\/gail-anderson\/"}]}},"_links":{"self":[{"href":"https:\/\/foojay.io\/wp-json\/wp\/v2\/posts\/45105","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/foojay.io\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/foojay.io\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/foojay.io\/wp-json\/wp\/v2\/users\/185"}],"replies":[{"embeddable":true,"href":"https:\/\/foojay.io\/wp-json\/wp\/v2\/comments?post=45105"}],"version-history":[{"count":0,"href":"https:\/\/foojay.io\/wp-json\/wp\/v2\/posts\/45105\/revisions"}],"wp:attachment":[{"href":"https:\/\/foojay.io\/wp-json\/wp\/v2\/media?parent=45105"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/foojay.io\/wp-json\/wp\/v2\/categories?post=45105"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/foojay.io\/wp-json\/wp\/v2\/tags?post=45105"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}