JavaFX on Android build (instructions)

Sebastian Rheinnecker sebastian.rheinnecker at yworks.com
Mon Dec 2 01:55:43 PST 2013


Hello Tomas,

I attached the patch for the FXActivity and DalvikLauncher classes. To 
make this work, you need to patch the build process for the created 
projects as well. I couldn't find where this is done in the repo, so I 
did this manually every time :-), this is also the reason why it isn't 
included in the patch file. This is not optimized so there is still a 
hardcoded reference to the application jar in here, perhaps you can 
generalize this:

custom_rules.xml:
target init-env:
       add  <property name="app.lib.dir" value="app-libs" />

target install-application:
change to
     <target name="-install-application" unless="app.uptodate">
     <mkdir dir="${app.lib.dir}"/>
         <copy todir="${app.lib.dir}">
             <fileset dir="${jfx.app.dist.dir}" includes="*.jar"/>
         </copy>
     </target>


build-extras.xml:
add
             <property name="output-dex-file" 
value="${out.absolute.dir}/app_dex/${dex.file.name}"/>
             <mkdir dir="${out.absolute.dir}/app_dex"/>
             <exec executable="${dx}" failonerror="true">
                 <arg value="--dex" />
                 <arg value="-JXmx2048m" />
                 <arg value="--output=${output-dex-file}" />
                 <extra-parameters />
                 <arg line="${verbose.option}" />
                 <arg line="${nolocals.option}" />
                 <arg path="${out.dex.input.absolute.dir}" />
                 <arg line="${app.lib.dir}/MyApplication.jar"/>
                 <external-libs />
             </exec>
             <jar destfile="${asset.absolute.dir}/Application_dex.jar"
                    basedir="${out.absolute.dir}/app_dex"
                    includes="${dex.file.name}" />

right after the first <exec executable="${dx}" .... />

I hope I didn't forget anything.

Kind regards,
Sebastian

Am 02.12.2013 10:27, schrieb tomas.brandalik:
> Hi Sebastian,
> please would you send the patch to me?
>
> thank you in advance
> -Tomas
>
> On 12/02/2013 09:55 AM, Sebastian Rheinnecker wrote:
>> Hi again,
>>
>> I just wanted to let you know that I solved the issue with the max 
>> method references in the dex files with using multiple dex files. To 
>> accomplish this I patched the classes FXActivity and DalvikLauncher 
>> to dynamically load the second dex file and its classes. I also 
>> patched the build process so that any application jar can be used for 
>> this. If anyone runs into the same issue, just contact me.
>>
>> Kind regards,
>> Sebastian Rheinnecker
>>
>> Am 26.11.2013 14:27, schrieb Sebastian Rheinnecker:
>>> Hi,
>>>
>>> I used your instructions with some tweaks to build a JavaFX 
>>> application for android on Windows. However, it turns out that 
>>> Android's dex format is putting a spoke into our wheel with the 65k 
>>> method reference limit. The JavaFX runtime itself already contains 
>>> 51k method references and because of that I was not yet able to 
>>> bring our full application showcase to android.
>>> So if some android expert could contribute to the project by showing 
>>> how to use multiple dex files, one containing the javafx runtime and 
>>> one for the application, that would be great. Any help on the 
>>> mailing list is appreciated as well.
>>>
>>> Kind regards,
>>> Sebastian Rheinnecker
>>>
>>> Am 16.11.2013 19:53, schrieb Johan Vos:
>>>> Hi,
>>>>
>>>> Since I (and others) think it is very important that we can have
>>>> JavaFX applications running on Android (and IOS, but in this mail I
>>>> limit myself to Android), I created a bitbucket project containing
>>>> code and build instructions to run JavaFX applications on Android.
>>>>
>>>> I didn't do much myself. Rather, this project is about providing easy
>>>> and consistent build instructions. I combined information from this
>>>> mailinglist with information I could find online, and with help from
>>>> the JavaFX team.
>>>>
>>>> The project is here: https://bitbucket.org/johanvos/jfx78
>>>>
>>>> After I gathered the information, it didn't cost me much time to get
>>>> HelloWorld running on my Samsung S3 mini. I never wrote an Android
>>>> project before, and I never used the Android SDK or NDK before I
>>>> started with this thing almost a week ago. Hence, it is not that hard
>>>> to create a JavaFX application on Android.
>>>>
>>>> This project is far from finished. There are a number of things that
>>>> have to be done:
>>>> * Improve build instructions
>>>> * Simplify build
>>>> * Fix bugs (e.g. touch-events are not processed yet)
>>>> * Manage the synchronization with the main JavaFX repo
>>>> * create plugins for IDE's or maven to automatically build the 
>>>> Android packages
>>>>
>>>> I want to open this project at this early stage, though, since I think
>>>> it is important to have more community input. Also, I want to give a
>>>> shout to the world that JavaFX on Android is not a dream.
>>>>
>>>> I hope many of you try out the instructions, improve them, correct
>>>> them, and test your applications on Android.
>>>>
>>>> Thanks a lot to all the people on this mailinglist for telling how
>>>> they were dealing with JavaFX on Android. Again, I didn't write much
>>>> code, but rather tried to combine the information and make it useful
>>>> for everybody.
>>>>
>>>> I know there is an intense debate about the role of Oracle on the
>>>> Android (and IOS) ports. Let me close with a similar situation. About
>>>> 17 years ago (sigh, time flies), I was involved with the port of Java
>>>> to Linux, as part of the Blackdown team. Initially, we didn't get much
>>>> help from Sun Microsystems (it was even not easy to get the latest
>>>> code). But once we showed that we could run the thing on Linux, and
>>>> that many developers were interested in it, Sun started to add
>>>> resources on this as well. Eventually, we became obsolete. I hope to
>>>> reach the obsolete stage on this project as well.
>>>>
>>>> Again, the project can be found 
>>>> here:https://bitbucket.org/johanvos/jfx78
>>>>
>>>> - Johan
>>>
>>>
>>
>>
>


-- 
Sebastian Rheinnecker
phone: +49 7071 9709050
fax: +49 7071 9709051

yWorks GmbH
Vor dem Kreuzberg 28
72070 Tuebingen
Germany
http://www.yworks.com
Managing Directors: Sebastian Müller, Michael Pfahler
Commercial Registry: Stuttgart, Germany, HRB 382340

-------------- next part --------------
# HG changeset patch
# User Sebastian Rheinnecker <sebastian.rheinnecker at yworks.com>
# Date 1385977278 -3600
# Node ID f4fc935ac56fe0226cc169a6d8651f69ec346955
# Parent  ace86ff6a0233960d6226e2ae4762783e579403f
use separate dex files for library and application

diff -r ace86ff6a023 -r f4fc935ac56f modules/graphics/src/android/java/com/oracle/dalvik/DalvikLauncher.java
--- a/modules/graphics/src/android/java/com/oracle/dalvik/DalvikLauncher.java	Wed Nov 20 19:23:14 2013 +0100
+++ b/modules/graphics/src/android/java/com/oracle/dalvik/DalvikLauncher.java	Mon Dec 02 10:41:18 2013 +0100
@@ -26,13 +26,18 @@
 package com.oracle.dalvik;
 
 import android.app.Activity;
+import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.util.Log;
+import dalvik.system.DexClassLoader;
+
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.util.Map.Entry;
 import java.util.Properties;
+import java.io.File;
+
 
 public class DalvikLauncher implements Launcher {
 
@@ -127,10 +132,22 @@
     private Class resolveApplicationClass()
 			throws PackageManager.NameNotFoundException, ClassNotFoundException {
 
-		Class clazz = null;
+      // Internal storage where the DexClassLoader writes the optimized dex file to.
+      final File optimizedDexOutputPath = activity.getDir("outdex", Context.MODE_PRIVATE);
+
+      final File dexInternalStoragePath = new File(activity.getDir("dex", Context.MODE_PRIVATE),
+          FXActivity.APPLICATION_DEX_NAME);
+
+      // Initialize the class loader with the secondary dex file.
+      DexClassLoader cl = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),
+          optimizedDexOutputPath.getAbsolutePath(),
+          null,
+          activity.getClassLoader());
+
+      Class clazz = null;
         String applicationClassName = metadata.getString(META_DATA_MAIN_CLASS);
 		if (applicationClassName != null && applicationClassName.length() > 0) {
-			clazz = Class.forName(applicationClassName);
+			clazz = cl.loadClass(applicationClassName);
 		}
 		return clazz;
 	}
diff -r ace86ff6a023 -r f4fc935ac56f modules/graphics/src/android/java/com/oracle/dalvik/FXActivity.java
--- a/modules/graphics/src/android/java/com/oracle/dalvik/FXActivity.java	Wed Nov 20 19:23:14 2013 +0100
+++ b/modules/graphics/src/android/java/com/oracle/dalvik/FXActivity.java	Mon Dec 02 10:41:18 2013 +0100
@@ -44,6 +44,14 @@
 import android.view.inputmethod.InputMethodManager;
 import android.widget.FrameLayout;
 
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
 public class FXActivity extends Activity implements SurfaceHolder.Callback, 
         SurfaceHolder.Callback2 {
 	
@@ -52,6 +60,8 @@
     private static final String GLASS_LENS_ANDROID_LIB      = "glass_lens_android";
     private static final String META_DATA_LAUNCHER_CLASS    = "launcher.class";
     private static final String DEFAULT_LAUNCHER_CLASS      = "com.oracle.dalvik.JavaSELauncher";
+    public static final String APPLICATION_DEX_NAME = "Application_dex.jar";
+    static final int BUF_SIZE = 8 * 1024;
     
     private static FXActivity   instance;
     private static Launcher     launcher;
@@ -80,7 +90,32 @@
         setContentView(mViewGroup);
         instance = this;     
         Log.v(TAG, "Loading glass native library.");
-        System.loadLibrary(GLASS_LENS_ANDROID_LIB);        
+        System.loadLibrary(GLASS_LENS_ANDROID_LIB);
+
+        // Before the secondary dex file can be processed by the DexClassLoader,
+        // it has to be first copied from asset resource to a storage location.
+        File dexInternalStoragePath = new File(getDir("dex", Context.MODE_PRIVATE),
+            APPLICATION_DEX_NAME);
+        BufferedInputStream bis;
+        OutputStream dexWriter;
+
+        try {
+          bis = new BufferedInputStream(getAssets().open(APPLICATION_DEX_NAME));
+          dexWriter = new BufferedOutputStream(
+              new FileOutputStream(dexInternalStoragePath));
+          byte[] buf = new byte[BUF_SIZE];
+          int len;
+          while((len = bis.read(buf, 0, BUF_SIZE)) > 0) {
+            dexWriter.write(buf, 0, len);
+          }
+          dexWriter.close();
+          bis.close();
+
+        } catch (FileNotFoundException e) {
+          e.printStackTrace();
+        } catch (IOException e) {
+          e.printStackTrace();
+        }
     }
     
     private Bundle getMetadata() {
@@ -105,7 +140,7 @@
             launcher.launchApp(this, metaData);
             
         } catch (Exception ex) {
-            throw new RuntimeException("Did not created correct launcher.", ex);
+            throw new RuntimeException("Did not create correct launcher.", ex);
         }
     }
     


More information about the openjfx-dev mailing list