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